Package namespace and Safe init cleanup for plperl UPDATE 3 [PATCH]
This is an update to the final plperl patch in the series from me.
Changes in the original patch:
- Moved internal functions out of main:: namespace
into PostgreSQL::InServer and PostgreSQL::InServer::safe
- Restructured Safe compartment setup code
to generalize and separate the data from the logic.
Neither change has any user visible effects.
Additional changes in the second version:
- Further generalized the 'what to load into Safe compartment' logic.
- Added the 'warnings' pragma to the list of modules to load into Safe.
So plperl functions can now "use warnings;" - added test for that.
- Added 'use 5.008001;' to plc_perlboot.pl as a run-time check to
complement the configure-time check added by Tom Lane recently.
Additional changes in this version:
- Rebased over recent HEAD plus "on_trusted_init" patch
- Made plc_safe_ok.pl code idempotent to avoid risk of problems
from repeated initialization attempts e.g. if on_trusted_init code
throws an exception so initialization doesn't complete.
- Fixed 'require strict' to enable 'caller' opcode
(needed for Perl >=5.10)
- Ensure Safe container opmask is restored even if @EvalInSafe code
throws an exception.
- Changed errmsg("didn't get a GLOB ...") to use errmsg_internal().
Tim.
Attachments:
plperl-nscleanup3.patchtext/x-patch; charset=us-asciiDownload
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index ebf9afd..0e7c65d 100644
*** a/src/pl/plperl/expected/plperl.out
--- b/src/pl/plperl/expected/plperl.out
*************** CONTEXT: PL/Perl anonymous code block
*** 577,579 ****
--- 577,584 ----
DO $do$ use strict; my $name = "foo"; my $ref = $$name; $do$ LANGUAGE plperl;
ERROR: Can't use string ("foo") as a SCALAR ref while "strict refs" in use at line 1.
CONTEXT: PL/Perl anonymous code block
+ -- check that we can "use warnings" (in this case to turn a warn into an error)
+ -- yields "ERROR: Useless use of length in void context"
+ DO $do$ use warnings FATAL => qw(void) ; length "abc" ; 1; $do$ LANGUAGE plperl;
+ ERROR: Useless use of length in void context at line 1.
+ CONTEXT: PL/Perl anonymous code block
diff --git a/src/pl/plperl/plc_perlboot.pl b/src/pl/plperl/plc_perlboot.pl
index 5d2e962..74b2a47 100644
*** a/src/pl/plperl/plc_perlboot.pl
--- b/src/pl/plperl/plc_perlboot.pl
***************
*** 1,26 ****
# $PostgreSQL$
PostgreSQL::InServer::Util::bootstrap();
use strict;
use warnings;
use vars qw(%_SHARED);
! sub ::plperl_warn {
(my $msg = shift) =~ s/\(eval \d+\) //g;
chomp $msg;
! &elog(&NOTICE, $msg);
}
! $SIG{__WARN__} = \&::plperl_warn;
! sub ::plperl_die {
(my $msg = shift) =~ s/\(eval \d+\) //g;
die $msg;
}
! $SIG{__DIE__} = \&::plperl_die;
! sub ::mkfuncsrc {
my ($name, $imports, $prolog, $src) = @_;
my $BEGIN = join "\n", map {
--- 1,30 ----
# $PostgreSQL$
+ use 5.008001;
+
PostgreSQL::InServer::Util::bootstrap();
+ package PostgreSQL::InServer;
+
use strict;
use warnings;
use vars qw(%_SHARED);
! sub plperl_warn {
(my $msg = shift) =~ s/\(eval \d+\) //g;
chomp $msg;
! &::elog(&::NOTICE, $msg);
}
! $SIG{__WARN__} = \&plperl_warn;
! sub plperl_die {
(my $msg = shift) =~ s/\(eval \d+\) //g;
die $msg;
}
! $SIG{__DIE__} = \&plperl_die;
! sub mkfuncsrc {
my ($name, $imports, $prolog, $src) = @_;
my $BEGIN = join "\n", map {
*************** sub ::mkfuncsrc {
*** 32,44 ****
$name =~ s/\\/\\\\/g;
$name =~ s/::|'/_/g; # avoid package delimiters
! return qq[ undef *{'$name'}; *{'$name'} = sub { $BEGIN $prolog $src } ];
}
# see also mksafefunc() in plc_safe_ok.pl
! sub ::mkunsafefunc {
no strict; # default to no strict for the eval
! my $ret = eval(::mkfuncsrc(@_));
$@ =~ s/\(eval \d+\) //g if $@;
return $ret;
}
--- 36,48 ----
$name =~ s/\\/\\\\/g;
$name =~ s/::|'/_/g; # avoid package delimiters
! return qq[ package main; undef *{'$name'}; *{'$name'} = sub { $BEGIN $prolog $src } ];
}
# see also mksafefunc() in plc_safe_ok.pl
! sub mkunsafefunc {
no strict; # default to no strict for the eval
! my $ret = eval(mkfuncsrc(@_));
$@ =~ s/\(eval \d+\) //g if $@;
return $ret;
}
*************** sub ::encode_array_literal {
*** 67,73 ****
sub ::encode_array_constructor {
my $arg = shift;
! return quote_nullable($arg)
if ref $arg ne 'ARRAY';
my $res = join ", ", map {
(ref $_) ? ::encode_array_constructor($_)
--- 71,77 ----
sub ::encode_array_constructor {
my $arg = shift;
! return ::quote_nullable($arg)
if ref $arg ne 'ARRAY';
my $res = join ", ", map {
(ref $_) ? ::encode_array_constructor($_)
diff --git a/src/pl/plperl/plc_safe_ok.pl b/src/pl/plperl/plc_safe_ok.pl
index e3666f2..b87284c 100644
*** a/src/pl/plperl/plc_safe_ok.pl
--- b/src/pl/plperl/plc_safe_ok.pl
***************
*** 1,43 ****
! # $PostgreSQL$
! use strict;
! use vars qw($PLContainer);
- $PLContainer = new Safe('PLPerl');
$PLContainer->permit_only(':default');
$PLContainer->permit(qw[:base_math !:base_io sort time require]);
- $PLContainer->share(qw[&elog &return_next
- &spi_query &spi_fetchrow &spi_cursor_close &spi_exec_query
- &spi_prepare &spi_exec_prepared &spi_query_prepared &spi_freeplan
- &DEBUG &LOG &INFO &NOTICE &WARNING &ERROR %_SHARED
- "e_literal "e_nullable "e_ident
- &encode_bytea &decode_bytea
- &encode_array_literal &encode_array_constructor
- &looks_like_number
- ]);
! # Load widely useful pragmas into the container to make them available.
! # (Temporarily enable caller here as work around for bug in perl 5.10,
! # which changed the way its Safe.pm works. It is quite safe, as caller is
! # informational only.)
! $PLContainer->permit(qw[caller]);
! ::safe_eval(q{
! require strict;
! require feature if $] >= 5.010000;
! 1;
! }) or die $@;
! $PLContainer->deny(qw[caller]);
! # called directly for plperl.on_trusted_init
! sub ::safe_eval {
my $ret = $PLContainer->reval(shift);
$@ =~ s/\(eval \d+\) //g if $@;
return $ret;
}
! sub ::mksafefunc {
! return ::safe_eval(::mkfuncsrc(@_));
}
--- 1,88 ----
+ package PostgreSQL::InServer::safe;
+ use strict;
+ use warnings;
+ use Safe;
! # @EvalInSafe = ( [ "string to eval", "extra,opcodes,to,allow" ], ...)
! # @ShareIntoSafe = ( [ from_class => \@symbols ], ...)
! use vars qw($PLContainer $SafeClass @EvalInSafe @ShareIntoSafe);
! # --- configuration ---
!
! # ensure we only alter the configuration variables once to avoid any
! # problems if this code is run multiple times due to an exception generated
! # from plperl.on_trusted_init code leaving the interp_state unchanged.
!
! if (not our $_init++) {
!
! # Load widely useful pragmas into the container to make them available.
! # These must be trusted to not expose a way to execute a string eval
! # or any kind of unsafe action that the untrusted code could exploit.
! # If in ANY doubt about a module then DO NOT add it to this list.
!
! unshift @EvalInSafe,
! [ 'require strict', 'caller' ],
! [ 'require Carp', 'caller,entertry' ], # load Carp before warnings
! [ 'require warnings', 'caller' ];
! push @EvalInSafe,
! [ 'require feature' ] if $] >= 5.010000;
!
! push @ShareIntoSafe, [
! main => [ qw(
! &elog &DEBUG &LOG &INFO &NOTICE &WARNING &ERROR
! &spi_query &spi_fetchrow &spi_cursor_close &spi_exec_query
! &spi_prepare &spi_exec_prepared &spi_query_prepared &spi_freeplan
! &return_next &_SHARED
! "e_literal "e_nullable "e_ident
! &encode_bytea &decode_bytea &looks_like_number
! &encode_array_literal &encode_array_constructor
! ) ],
! ];
! }
!
! # --- create and initialize a new container ---
!
! $SafeClass ||= 'Safe';
! $PLContainer = $SafeClass->new('PostgreSQL::InServer::safe_container');
$PLContainer->permit_only(':default');
$PLContainer->permit(qw[:base_math !:base_io sort time require]);
! for my $do (@EvalInSafe) {
! my $perform = sub { # private closure
! my ($container, $src, $ops) = @_;
! my $mask = $container->mask;
! $container->permit(split /\s*,\s*/, $ops);
! my $ok = safe_eval("$src; 1");
! $container->mask($mask);
! main::elog(main::ERROR(), "$src failed: $@") unless $ok;
! };
! my $ops = $do->[1] || '';
! # For old perls we add entereval if entertry is listed
! # due to http://rt.perl.org/rt3/Ticket/Display.html?id=70970
! # Testing with a recent perl (>=5.11.4) ensures this doesn't
! # allow any use of actual entereval (eval "...") opcodes.
! $ops = "entereval,$ops"
! if $] < 5.011004 and $ops =~ /\bentertry\b/;
!
! $perform->($PLContainer, $do->[0], $ops);
! }
!
! $PLContainer->share_from(@$_) for @ShareIntoSafe;
!
!
! # --- runtime interface ---
!
! # called directly for plperl.on_trusted_init and @EvalInSafe
! sub safe_eval {
my $ret = $PLContainer->reval(shift);
$@ =~ s/\(eval \d+\) //g if $@;
return $ret;
}
! sub mksafefunc {
! return safe_eval(PostgreSQL::InServer::mkfuncsrc(@_));
}
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 2b6ec2f..44cb5a3 100644
*** a/src/pl/plperl/plperl.c
--- b/src/pl/plperl/plperl.c
*************** plperl_trusted_init(void)
*** 724,730 ****
XPUSHs(sv_2mortal(newSVstring(plperl_on_trusted_init)));
PUTBACK;
! call_pv("::safe_eval", G_VOID);
SPAGAIN;
if (SvTRUE(ERRSV))
--- 724,730 ----
XPUSHs(sv_2mortal(newSVstring(plperl_on_trusted_init)));
PUTBACK;
! call_pv("PostgreSQL::InServer::safe::safe_eval", G_VOID);
SPAGAIN;
if (SvTRUE(ERRSV))
*************** plperl_create_sub(plperl_proc_desc *prod
*** 1269,1275 ****
* errors properly. Perhaps it's because there's another level of eval
* inside mksafefunc?
*/
! compile_sub = (trusted) ? "::mksafefunc" : "::mkunsafefunc";
count = perl_call_pv(compile_sub, G_SCALAR | G_EVAL | G_KEEPERR);
SPAGAIN;
--- 1269,1277 ----
* errors properly. Perhaps it's because there's another level of eval
* inside mksafefunc?
*/
! compile_sub = (trusted)
! ? "PostgreSQL::InServer::safe::mksafefunc"
! : "PostgreSQL::InServer::mkunsafefunc";
count = perl_call_pv(compile_sub, G_SCALAR | G_EVAL | G_KEEPERR);
SPAGAIN;
*************** plperl_create_sub(plperl_proc_desc *prod
*** 1297,1303 ****
{
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
! errmsg("didn't get a GLOB from compiling %s via %s", prodesc->proname, compile_sub)));
}
prodesc->reference = newSVsv(subref);
--- 1299,1305 ----
{
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
! errmsg_internal("didn't get a GLOB from compiling %s via %s", prodesc->proname, compile_sub)));
}
prodesc->reference = newSVsv(subref);
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index e6ef5f0..905e918 100644
*** a/src/pl/plperl/sql/plperl.sql
--- b/src/pl/plperl/sql/plperl.sql
*************** DO $$ use blib; $$ LANGUAGE plperl;
*** 378,380 ****
--- 378,384 ----
-- runtime error: "Can't use string ("foo") as a SCALAR ref while "strict refs" in use
DO $do$ use strict; my $name = "foo"; my $ref = $$name; $do$ LANGUAGE plperl;
+ -- check that we can "use warnings" (in this case to turn a warn into an error)
+ -- yields "ERROR: Useless use of length in void context"
+ DO $do$ use warnings FATAL => qw(void) ; length "abc" ; 1; $do$ LANGUAGE plperl;
+
On Sat, Jan 30, 2010 at 16:16, Tim Bunce <Tim.Bunce@pobox.com> wrote:
This is an update to the final plperl patch in the series from me.
Changes in the original patch:
plc_safe_ok.pl seems to loose its CVS $PostgreSQL$ keyword.
- Ensure Safe container opmask is restored even if @EvalInSafe code
throws an exception.
Maybe we could be a bit smarter about this and avoid the problem? Im
thinking either (for @ShareIntoSafe as well):
1) always reinit @EvalInSafe at the top of plc_safe_ok.pl
our @EvalInSafe = [ ]; ....
Seems like a non-starter, why have the 'our' at all?
2)Change EvalInSafe to be a hash so at least the problem with
duplicates goes away:
$EvalInSafe{'strict'} = 'caller';
Then again maybe its fine the way it is. Thoughts?
This makes me think maybe we should not expose it at all. Its not
like you can tell people oh you have something you need in your plperl
safe? Just stick it in @PostgreSQL::InServer::safe::EvalInSafe. As
we might change this *undocumented* interface in the future.
That being said *im* ok with it. Its useful from a debug standpoint.
Thoughts?
On Sat, Jan 30, 2010 at 06:38:59PM -0700, Alex Hunsaker wrote:
On Sat, Jan 30, 2010 at 16:16, Tim Bunce <Tim.Bunce@pobox.com> wrote:
This is an update to the final plperl patch in the series from me.
Changes in the original patch:
plc_safe_ok.pl seems to loose its CVS $PostgreSQL$ keyword.
Probably a slip-up when I merged the changes from HEAD up through my
chain of branches.
- Ensure Safe container opmask is restored even if @EvalInSafe code
�throws an exception.Maybe we could be a bit smarter about this and avoid the problem?
Im thinking either (for @ShareIntoSafe as well):1) always reinit @EvalInSafe at the top of plc_safe_ok.pl
our @EvalInSafe = [ ]; ....Seems like a non-starter, why have the 'our' at all?
Yeap.
2)Change EvalInSafe to be a hash so at least the problem with
duplicates goes away:
$EvalInSafe{'strict'} = 'caller';Then again maybe its fine the way it is. Thoughts?
A better approach would be for the plperl.c code to keep track of
initialization with a finer granularity. Specifically track the fact
that plc_safe_ok.pl ran ok so not re-run it if on_trusted_init fails.
But that would be a more invasive change for no significant gain so
didn't seem appropriate at this point.
The current code works fine, and behaves well in failure modes, so I
think it's okay the way it is.
I hope to work on plperl.c some more for the 9.1 release (if my
employer's generosity continues). Mainly to switch to using
PERL_NO_GET_CONTEXT to simplify state management and improve
performance (getting rid of the many many hidden calls to
pthread_getspecific). That would be a good time to rework this area.
This makes me think maybe we should not expose it at all. Its not
like you can tell people oh you have something you need in your plperl
safe? Just stick it in @PostgreSQL::InServer::safe::EvalInSafe. As
we might change this *undocumented* interface in the future.That being said *im* ok with it. Its useful from a debug standpoint.
Yes. And, as I mentioned previously, I expect people like myself, David
Wheeler, and others to experiment with the undocumented functionality
and define and document a good API to it for the 9.1 release.
I'd much rather get this change in than shoot for a larger change that
doesn't get committed due to long-running discussions. (Which seems
more likely as Andrew's going to be less available for the rest of the
commitfest.)
Tim.
p.s. If there is interest in defining a documented API (for DBAs to
control what gets loaded into Safe and shared with it) for *9.0*
then that could be worked on, once this pach is in, ready for the
next commitfest.
On Mon, Feb 1, 2010 at 5:58 AM, Tim Bunce <Tim.Bunce@pobox.com> wrote:
p.s. If there is interest in defining a documented API (for DBAs to
control what gets loaded into Safe and shared with it) for *9.0*
then that could be worked on, once this pach is in, ready for the
next commitfest.
This is the last CommitFest for 9.0. It's time to wind down
development on this release and work on trying to get the release
stabilized and out the door.
This isn't intended as a disparagement of the work you're doing; I've
thought about using PL/perl in the past and decided against it exactly
because of some of the issues you're now fixing. But we're really out
of time to get things done for 9.0.
...Robert
On Mon, Feb 01, 2010 at 10:46:10AM -0500, Robert Haas wrote:
On Mon, Feb 1, 2010 at 5:58 AM, Tim Bunce <Tim.Bunce@pobox.com> wrote:
p.s. If there is interest in defining a documented API (for DBAs to
control what gets loaded into Safe and shared with it) for *9.0*
then that could be worked on, once this pach is in, ready for the
next commitfest.This is the last CommitFest for 9.0. It's time to wind down
development on this release and work on trying to get the release
stabilized and out the door.This isn't intended as a disparagement of the work you're doing; I've
thought about using PL/perl in the past and decided against it exactly
because of some of the issues you're now fixing. But we're really out
of time to get things done for 9.0.
Understood Robert. No problem. (You can't blame me for trying ;-)
Tim.
On Mon, Feb 1, 2010 at 03:58, Tim Bunce <Tim.Bunce@pobox.com> wrote:
On Sat, Jan 30, 2010 at 06:38:59PM -0700, Alex Hunsaker wrote:
plc_safe_ok.pl seems to loose its CVS $PostgreSQL$ keyword.
Probably a slip-up when I merged the changes from HEAD up through my
chain of branches.
Can you send an updated patch? I think Andrew will probably fix it up
anyway but better safe than sorry.
That being said *im* ok with it. Its useful from a debug standpoint.
Yes. And, as I mentioned previously, I expect people like myself, David
Wheeler, and others to experiment with the undocumented functionality
and define and document a good API to it for the 9.1 release.
Huh, I missed that.
I'd much rather get this change in than shoot for a larger change that
doesn't get committed due to long-running discussions. (Which seems
more likely as Andrew's going to be less available for the rest of the
commitfest.)
Plus its hard to get people to agree on anything GUCy (my new favorite
pun) thats not well thought out and tested.
Anyway yes I agree, but I thought I should at least raise it for
discussion. You'll notice the patch has been marked "Ready for
Commiter" this whole time. =)
On Mon, Feb 01, 2010 at 07:53:05PM -0700, Alex Hunsaker wrote:
On Mon, Feb 1, 2010 at 03:58, Tim Bunce <Tim.Bunce@pobox.com> wrote:
On Sat, Jan 30, 2010 at 06:38:59PM -0700, Alex Hunsaker wrote:
plc_safe_ok.pl seems to loose its CVS $PostgreSQL$ keyword.
Probably a slip-up when I merged the changes from HEAD up through my
chain of branches.Can you send an updated patch? I think Andrew will probably fix it up
anyway but better safe than sorry.
Attached. I'll add it to the commitfest.
Anyway yes I agree, but I thought I should at least raise it for
discussion. You'll notice the patch has been marked "Ready for
Commiter" this whole time. =)
Thanks.
Tim.
Attachments:
plperl-nscleanup4.patchtext/x-patch; charset=us-asciiDownload
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index ebf9afd..0e7c65d 100644
*** a/src/pl/plperl/expected/plperl.out
--- b/src/pl/plperl/expected/plperl.out
*************** CONTEXT: PL/Perl anonymous code block
*** 577,579 ****
--- 577,584 ----
DO $do$ use strict; my $name = "foo"; my $ref = $$name; $do$ LANGUAGE plperl;
ERROR: Can't use string ("foo") as a SCALAR ref while "strict refs" in use at line 1.
CONTEXT: PL/Perl anonymous code block
+ -- check that we can "use warnings" (in this case to turn a warn into an error)
+ -- yields "ERROR: Useless use of length in void context"
+ DO $do$ use warnings FATAL => qw(void) ; length "abc" ; 1; $do$ LANGUAGE plperl;
+ ERROR: Useless use of length in void context at line 1.
+ CONTEXT: PL/Perl anonymous code block
diff --git a/src/pl/plperl/plc_perlboot.pl b/src/pl/plperl/plc_perlboot.pl
index 5d2e962..74b2a47 100644
*** a/src/pl/plperl/plc_perlboot.pl
--- b/src/pl/plperl/plc_perlboot.pl
***************
*** 1,26 ****
# $PostgreSQL$
PostgreSQL::InServer::Util::bootstrap();
use strict;
use warnings;
use vars qw(%_SHARED);
! sub ::plperl_warn {
(my $msg = shift) =~ s/\(eval \d+\) //g;
chomp $msg;
! &elog(&NOTICE, $msg);
}
! $SIG{__WARN__} = \&::plperl_warn;
! sub ::plperl_die {
(my $msg = shift) =~ s/\(eval \d+\) //g;
die $msg;
}
! $SIG{__DIE__} = \&::plperl_die;
! sub ::mkfuncsrc {
my ($name, $imports, $prolog, $src) = @_;
my $BEGIN = join "\n", map {
--- 1,30 ----
# $PostgreSQL$
+ use 5.008001;
+
PostgreSQL::InServer::Util::bootstrap();
+ package PostgreSQL::InServer;
+
use strict;
use warnings;
use vars qw(%_SHARED);
! sub plperl_warn {
(my $msg = shift) =~ s/\(eval \d+\) //g;
chomp $msg;
! &::elog(&::NOTICE, $msg);
}
! $SIG{__WARN__} = \&plperl_warn;
! sub plperl_die {
(my $msg = shift) =~ s/\(eval \d+\) //g;
die $msg;
}
! $SIG{__DIE__} = \&plperl_die;
! sub mkfuncsrc {
my ($name, $imports, $prolog, $src) = @_;
my $BEGIN = join "\n", map {
*************** sub ::mkfuncsrc {
*** 32,44 ****
$name =~ s/\\/\\\\/g;
$name =~ s/::|'/_/g; # avoid package delimiters
! return qq[ undef *{'$name'}; *{'$name'} = sub { $BEGIN $prolog $src } ];
}
# see also mksafefunc() in plc_safe_ok.pl
! sub ::mkunsafefunc {
no strict; # default to no strict for the eval
! my $ret = eval(::mkfuncsrc(@_));
$@ =~ s/\(eval \d+\) //g if $@;
return $ret;
}
--- 36,48 ----
$name =~ s/\\/\\\\/g;
$name =~ s/::|'/_/g; # avoid package delimiters
! return qq[ package main; undef *{'$name'}; *{'$name'} = sub { $BEGIN $prolog $src } ];
}
# see also mksafefunc() in plc_safe_ok.pl
! sub mkunsafefunc {
no strict; # default to no strict for the eval
! my $ret = eval(mkfuncsrc(@_));
$@ =~ s/\(eval \d+\) //g if $@;
return $ret;
}
*************** sub ::encode_array_literal {
*** 67,73 ****
sub ::encode_array_constructor {
my $arg = shift;
! return quote_nullable($arg)
if ref $arg ne 'ARRAY';
my $res = join ", ", map {
(ref $_) ? ::encode_array_constructor($_)
--- 71,77 ----
sub ::encode_array_constructor {
my $arg = shift;
! return ::quote_nullable($arg)
if ref $arg ne 'ARRAY';
my $res = join ", ", map {
(ref $_) ? ::encode_array_constructor($_)
diff --git a/src/pl/plperl/plc_safe_ok.pl b/src/pl/plperl/plc_safe_ok.pl
index e3666f2..5d0fc93 100644
*** a/src/pl/plperl/plc_safe_ok.pl
--- b/src/pl/plperl/plc_safe_ok.pl
***************
*** 1,43 ****
-
# $PostgreSQL$
use strict;
! use vars qw($PLContainer);
- $PLContainer = new Safe('PLPerl');
$PLContainer->permit_only(':default');
$PLContainer->permit(qw[:base_math !:base_io sort time require]);
- $PLContainer->share(qw[&elog &return_next
- &spi_query &spi_fetchrow &spi_cursor_close &spi_exec_query
- &spi_prepare &spi_exec_prepared &spi_query_prepared &spi_freeplan
- &DEBUG &LOG &INFO &NOTICE &WARNING &ERROR %_SHARED
- "e_literal "e_nullable "e_ident
- &encode_bytea &decode_bytea
- &encode_array_literal &encode_array_constructor
- &looks_like_number
- ]);
! # Load widely useful pragmas into the container to make them available.
! # (Temporarily enable caller here as work around for bug in perl 5.10,
! # which changed the way its Safe.pm works. It is quite safe, as caller is
! # informational only.)
! $PLContainer->permit(qw[caller]);
! ::safe_eval(q{
! require strict;
! require feature if $] >= 5.010000;
! 1;
! }) or die $@;
! $PLContainer->deny(qw[caller]);
! # called directly for plperl.on_trusted_init
! sub ::safe_eval {
my $ret = $PLContainer->reval(shift);
$@ =~ s/\(eval \d+\) //g if $@;
return $ret;
}
! sub ::mksafefunc {
! return ::safe_eval(::mkfuncsrc(@_));
}
--- 1,91 ----
# $PostgreSQL$
+ package PostgreSQL::InServer::safe;
+
use strict;
! use warnings;
! use Safe;
!
! # @EvalInSafe = ( [ "string to eval", "extra,opcodes,to,allow" ], ...)
! # @ShareIntoSafe = ( [ from_class => \@symbols ], ...)
! use vars qw($PLContainer $SafeClass @EvalInSafe @ShareIntoSafe);
!
! # --- configuration ---
!
! # ensure we only alter the configuration variables once to avoid any
! # problems if this code is run multiple times due to an exception generated
! # from plperl.on_trusted_init code leaving the interp_state unchanged.
!
! if (not our $_init++) {
!
! # Load widely useful pragmas into the container to make them available.
! # These must be trusted to not expose a way to execute a string eval
! # or any kind of unsafe action that the untrusted code could exploit.
! # If in ANY doubt about a module then DO NOT add it to this list.
!
! unshift @EvalInSafe,
! [ 'require strict', 'caller' ],
! [ 'require Carp', 'caller,entertry' ], # load Carp before warnings
! [ 'require warnings', 'caller' ];
! push @EvalInSafe,
! [ 'require feature' ] if $] >= 5.010000;
!
! push @ShareIntoSafe, [
! main => [ qw(
! &elog &DEBUG &LOG &INFO &NOTICE &WARNING &ERROR
! &spi_query &spi_fetchrow &spi_cursor_close &spi_exec_query
! &spi_prepare &spi_exec_prepared &spi_query_prepared &spi_freeplan
! &return_next &_SHARED
! "e_literal "e_nullable "e_ident
! &encode_bytea &decode_bytea &looks_like_number
! &encode_array_literal &encode_array_constructor
! ) ],
! ];
! }
!
! # --- create and initialize a new container ---
!
! $SafeClass ||= 'Safe';
! $PLContainer = $SafeClass->new('PostgreSQL::InServer::safe_container');
$PLContainer->permit_only(':default');
$PLContainer->permit(qw[:base_math !:base_io sort time require]);
! for my $do (@EvalInSafe) {
! my $perform = sub { # private closure
! my ($container, $src, $ops) = @_;
! my $mask = $container->mask;
! $container->permit(split /\s*,\s*/, $ops);
! my $ok = safe_eval("$src; 1");
! $container->mask($mask);
! main::elog(main::ERROR(), "$src failed: $@") unless $ok;
! };
! my $ops = $do->[1] || '';
! # For old perls we add entereval if entertry is listed
! # due to http://rt.perl.org/rt3/Ticket/Display.html?id=70970
! # Testing with a recent perl (>=5.11.4) ensures this doesn't
! # allow any use of actual entereval (eval "...") opcodes.
! $ops = "entereval,$ops"
! if $] < 5.011004 and $ops =~ /\bentertry\b/;
!
! $perform->($PLContainer, $do->[0], $ops);
! }
!
! $PLContainer->share_from(@$_) for @ShareIntoSafe;
!
!
! # --- runtime interface ---
!
! # called directly for plperl.on_trusted_init and @EvalInSafe
! sub safe_eval {
my $ret = $PLContainer->reval(shift);
$@ =~ s/\(eval \d+\) //g if $@;
return $ret;
}
! sub mksafefunc {
! return safe_eval(PostgreSQL::InServer::mkfuncsrc(@_));
}
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 2b6ec2f..44cb5a3 100644
*** a/src/pl/plperl/plperl.c
--- b/src/pl/plperl/plperl.c
*************** plperl_trusted_init(void)
*** 724,730 ****
XPUSHs(sv_2mortal(newSVstring(plperl_on_trusted_init)));
PUTBACK;
! call_pv("::safe_eval", G_VOID);
SPAGAIN;
if (SvTRUE(ERRSV))
--- 724,730 ----
XPUSHs(sv_2mortal(newSVstring(plperl_on_trusted_init)));
PUTBACK;
! call_pv("PostgreSQL::InServer::safe::safe_eval", G_VOID);
SPAGAIN;
if (SvTRUE(ERRSV))
*************** plperl_create_sub(plperl_proc_desc *prod
*** 1269,1275 ****
* errors properly. Perhaps it's because there's another level of eval
* inside mksafefunc?
*/
! compile_sub = (trusted) ? "::mksafefunc" : "::mkunsafefunc";
count = perl_call_pv(compile_sub, G_SCALAR | G_EVAL | G_KEEPERR);
SPAGAIN;
--- 1269,1277 ----
* errors properly. Perhaps it's because there's another level of eval
* inside mksafefunc?
*/
! compile_sub = (trusted)
! ? "PostgreSQL::InServer::safe::mksafefunc"
! : "PostgreSQL::InServer::mkunsafefunc";
count = perl_call_pv(compile_sub, G_SCALAR | G_EVAL | G_KEEPERR);
SPAGAIN;
*************** plperl_create_sub(plperl_proc_desc *prod
*** 1297,1303 ****
{
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
! errmsg("didn't get a GLOB from compiling %s via %s", prodesc->proname, compile_sub)));
}
prodesc->reference = newSVsv(subref);
--- 1299,1305 ----
{
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
! errmsg_internal("didn't get a GLOB from compiling %s via %s", prodesc->proname, compile_sub)));
}
prodesc->reference = newSVsv(subref);
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index e6ef5f0..905e918 100644
*** a/src/pl/plperl/sql/plperl.sql
--- b/src/pl/plperl/sql/plperl.sql
*************** DO $$ use blib; $$ LANGUAGE plperl;
*** 378,380 ****
--- 378,384 ----
-- runtime error: "Can't use string ("foo") as a SCALAR ref while "strict refs" in use
DO $do$ use strict; my $name = "foo"; my $ref = $$name; $do$ LANGUAGE plperl;
+ -- check that we can "use warnings" (in this case to turn a warn into an error)
+ -- yields "ERROR: Useless use of length in void context"
+ DO $do$ use warnings FATAL => qw(void) ; length "abc" ; 1; $do$ LANGUAGE plperl;
+