Subject: | patch to integrate mprotect(2) in Sys::Mmap::Simple |
Hi Leon,
I think it would be a nice feature to be able to change memory
protection bits for mmapped segments. The enclosed patch implements it
on linux. It works with 5.10.0 on OpenSuse 11.1 and with 5.8.8 on
OpenSuse 10.2.
I have added the tests in a separate file, t/11-protect.t. You may
wonder why I use fork for the PROT_NONE test. Well, my first version
looked this way:
{
my $sig=0;
local $SIG{SEGV}=sub {die "SIGSEGV"};
protect $mmaped, PROT_NONE;
throws_ok { my $x=$mmaped } qr/SIGSEGV/, 'Got SIGSEGV';
}
But it turns out that perl 5.8.8 cannot catch SIGSEGV this way. It ends
up in an endless loop.
I don't have windows. I don't even know if it's possible to change
memory protection on windows but I believe so. So, that's up to you. And
thank you for the module!
Torsten
Subject: | protect.patch |
--- Sys-Mmap-Simple-0.13/./t/11-protect.t 2009-06-16 15:49:04.000000000 +0200
+++ Sys-Mmap-Simple-0.13.new/./t/11-protect.t 2009-06-16 15:48:30.000000000 +0200
@@ -0,0 +1,41 @@
+#!perl
+
+use strict;
+use warnings;
+use Sys::Mmap::Simple qw/:map protect :constants/;
+use IO::Handle;
+use Test::More tests => 9;
+use Test::Warn;
+use Test::Exception;
+use POSIX 'SIGSEGV';
+
+open my $copy, "+<", undef or die "Couldn't create tempfile: $!";
+$copy->autoflush(1);
+print $copy "0123456789"x10;
+
+ok(map_handle(my $mmaped, $copy, '+<'), "map succeeded");
+
+my $howmany=$mmaped=~tr/9/_/;
+is($mmaped, "012345678_"x10, "$howmany characters exchanged");
+
+protect $mmaped; # default protection is PROT_READ
+throws_ok { $mmaped=~tr/_/:/ } qr/Modification of a read-only value attempted/, 'now read only';
+is($mmaped, "012345678_"x10, "still the same value");
+
+protect $mmaped, PROT_WRITE|PROT_READ;
+lives_ok { $mmaped=~tr/_/:/ } 'now writable again';
+is($mmaped, "012345678:"x10, "written");
+
+my $pid;
+select undef, undef, undef, 0.1 unless defined($pid=fork);
+if( $pid ) {
+ waitpid $pid, 0;
+ is $?, SIGSEGV, 'got SIGSEGV as expected';
+} else {
+ protect $mmaped, PROT_NONE;
+ my $x=$mmaped;
+}
+
+protect $mmaped, PROT_WRITE|PROT_READ;
+lives_ok { $mmaped=~tr/:/9/ } 'now writable again';
+is($mmaped, "0123456789"x10, "written");
--- Sys-Mmap-Simple-0.13/./MANIFEST 2009-02-16 21:47:09.000000000 +0100
+++ Sys-Mmap-Simple-0.13.new/./MANIFEST 2009-06-16 15:15:59.000000000 +0200
@@ -9,7 +9,8 @@
README A file explaning what this module does, and how to build it
t/00-load.t A test checking if the module loads at all
t/10-basics.t Unit tests for the main mapping functionality
-t/20-errors.t Unit tests for erroneous situations
+t/11-protect.t Unit tests for erroneous situations
+t/20-errors.t Unit tests for protect functionality
t/20-threads.t Unit tests for the thread synchronization functions
t/pod-coverage.t A test checking documentation coverage
t/pod.t A test doing pod validation
--- Sys-Mmap-Simple-0.13/./lib/Sys/Mmap/Simple.pm 2009-02-16 21:47:09.000000000 +0100
+++ Sys-Mmap-Simple-0.13.new/./lib/Sys/Mmap/Simple.pm 2009-06-16 12:30:39.000000000 +0200
@@ -28,7 +28,7 @@
my %export_data = (
'map' => [qw/map_handle map_file map_anonymous unmap sys_map/],
- extra => [qw/remap sync pin unpin advise page_size/],
+ extra => [qw/remap sync pin unpin advise page_size protect/],
'lock' => [qw/locked wait_until notify broadcast/],
);
@@ -226,6 +226,10 @@
=back
+=item * protect $lvalue, $protection
+
+see the L<mprotect(2)> manpage
+
=back
=head2 Locking
--- Sys-Mmap-Simple-0.13/./lib/Sys/Mmap/Simple.xs 2009-02-16 21:47:09.000000000 +0100
+++ Sys-Mmap-Simple-0.13.new/./lib/Sys/Mmap/Simple.xs 2009-06-16 13:08:18.000000000 +0200
@@ -408,6 +408,30 @@
#endif
ST(0) = YES;
+SV*
+protect(var, prot = PROT_READ)
+ SV* var = deref_var(aTHX_ ST(0));
+ int prot;
+ PROTOTYPE: \$@
+ CODE:
+ MAGIC* magic;
+ struct mmap_info* info;
+
+ if (!SvMAGICAL(var) || (magic = mg_find(var, PERL_MAGIC_uvar)) == NULL || magic->mg_private != MMAP_MAGIC_NUMBER)
+ Perl_croak(aTHX_ "Could not protect: this variable is not memory mapped");
+ info = (struct mmap_info*) magic->mg_ptr;
+ if (mprotect(info->real_address, info->real_length, prot ) == -1)
+ croak_sys(aTHX_ "Could not protect: %s");
+ if (prot & PROT_WRITE) {
+ magic->mg_virtual=&mmap_write_table;
+ SvREADONLY_off(var);
+ } else {
+ magic->mg_virtual=&mmap_read_table;
+ SvREADONLY_on(var);
+ }
+
+ ST(0) = YES;
+
void
locked(block, var)