Skip Menu |

This queue is for tickets about the Sys-Mmap-Simple CPAN distribution.

Report information
The Basics
Id: 47006
Status: resolved
Priority: 0/
Queue: Sys-Mmap-Simple

People
Owner: Nobody in particular
Requestors: OPI [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Wishlist
Broken in: 0.13
Fixed in: (no value)



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)
Hi Torsten, I was already considering adding that, but I wasn't sure it had any use cases. I will add a protect function to the next version, but probably using a friendlier interface (I don't like exporting constants). Also, I've renamed Sys::Mmap::Simple to File::Map recently, so I'm going to add it there and deprecate S::M::S. Leon On Tue Jun 16 10:12:16 2009, OPI wrote: Show quoted text
> 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