From 7447ddb7ffc11854e481aaae0aaa3546f287443b Mon Sep 17 00:00:00 2001 From: Ryan Voots Date: Thu, 4 May 2017 15:19:45 -0700 Subject: [PATCH] Found the problem with PID 1 --- lib/Sys/Linux/Mount.pm | 70 ++++++++++++++++++++------------------ lib/Sys/Linux/Mount.xs | 2 +- lib/Sys/Linux/Namespace.pm | 32 ++++++++--------- lib/Sys/Linux/Unshare.pm | 3 +- t/02-namespace.t | 9 ++++- 5 files changed, 64 insertions(+), 52 deletions(-) diff --git a/lib/Sys/Linux/Mount.pm b/lib/Sys/Linux/Mount.pm index 3ee12a0..d45c883 100644 --- a/lib/Sys/Linux/Mount.pm +++ b/lib/Sys/Linux/Mount.pm @@ -2,7 +2,7 @@ package Sys::Linux::Mount; use strict; use warnings; -use Carp qw/carp/; +use Carp qw/croak/; require Exporter; our @ISA = qw/Exporter/; @@ -10,37 +10,7 @@ require XSLoader; XSLoader::load(); -my @mount_consts = qw/MS_RDONLY MS_NOSUID MS_NODEV MS_NOEXEC MS_SYNCHRONOUS MS_REMOUNT MS_MANDLOCK MS_DIRSYNC MS_NOATIME MS_NODIRATIME MS_BIND MS_MOVE MS_REC MS_SILENT MS_POSIXACL MS_UNBINDABLE MS_PRIVATE MS_SLAVE MS_SHARED MS_RELATIME MS_KERNMOUNT MS_I_VERSION MS_STRICTATIME MS_LAZYTIME MS_ACTIVE MS_NOUSER/; - -our @EXPORT_OK = (@mount_consts, qw/mount umount/); - -our %EXPORT_TAGS = ( - 'consts' => \@mount_consts, - 'all' => [@mount_consts, qw/mount umount/], -); - -sub mount { - my ($source, $target, $filesystem, $flags, $options_hr) = @_; - - my $options_str = join ',', map {"$_=".$options_hr->{$_}} keys %$options_hr; - - my $ret = _mount_sys($source//"", $target//"", $filesystem//"", $flags//0, $options_str//""); - - if ($ret != 0) { - carp "mount failed: $ret $!"; - } - - return 1; -} - -sub umount { - my ($target) = @_; - - carp "No argument given to umount()" unless $target; - - my $ret = _umount_sys($target); - return 1; -} +my @mount_consts = qw/MS_RDONLY MS_NOSUID MS_NODEV MS_NOEXEC MS_SYNCHRONOUS MS_REMOUNT MS_MANDLOCK MS_DIRSYNC MS_NOATIME MS_NODIRATIME MS_BIND MS_MOVE MS_REC MS_SILENT MS_POSIXACL MS_UNBINDABLE MS_PRIVATE MS_SLAVE MS_SHARED MS_RELATIME MS_KERNMOUNT MS_I_VERSION MS_STRICTATIME MS_LAZYTIME MS_ACTIVE MS_NOUSER MS_MGC_VAL/; use constant {MS_RDONLY => 1, MS_NOSUID => 2, @@ -67,6 +37,40 @@ use constant {MS_RDONLY => 1, MS_STRICTATIME => 1 << 24, MS_LAZYTIME => 1 << 25, MS_ACTIVE => 1 << 30, - MS_NOUSER => 1 << 31}; + MS_NOUSER => 1 << 31, + MS_MGC_VAL => 0xc0ed0000 # /* Magic flag number to indicate "new" flags */ +}; +our @EXPORT_OK = (@mount_consts, qw/mount umount/); + +our %EXPORT_TAGS = ( + 'consts' => \@mount_consts, + 'all' => [@mount_consts, qw/mount umount/], +); + +sub mount { + my ($source, $target, $filesystem, $flags, $options_hr) = @_; + + my $options_str; + if ($options_hr) { + $options_str = join ',', map {"$_=".$options_hr->{$_}} keys %$options_hr; + } + + my $ret = _mount_sys($source, $target, $filesystem, $flags//0xc0ed0000, $options_str); + + if ($ret != 0) { + croak "mount failed: $ret $!"; + } + + return 1; +} + +sub umount { + my ($target) = @_; + + croak "No argument given to umount()" unless $target; + + my $ret = _umount_sys($target); + return 1; +} 1; diff --git a/lib/Sys/Linux/Mount.xs b/lib/Sys/Linux/Mount.xs index 415b86f..414ee61 100644 --- a/lib/Sys/Linux/Mount.xs +++ b/lib/Sys/Linux/Mount.xs @@ -15,7 +15,7 @@ PROTOTYPES: ENABLE SV *_mount_sys(const char *source, const char *target, const char *filesystem, unsigned long mountflags, const char *data) CODE: ST(0) = sv_newmortal(); - sv_setiv(ST(0), mount(source, target, filesystem, mountflags, (const void *) data)); + sv_setiv(ST(0), mount(source, target, filesystem, mountflags, NULL)); SV *_umount_sys(const char *target) CODE: diff --git a/lib/Sys/Linux/Namespace.pm b/lib/Sys/Linux/Namespace.pm index d304483..518f281 100644 --- a/lib/Sys/Linux/Namespace.pm +++ b/lib/Sys/Linux/Namespace.pm @@ -9,7 +9,9 @@ use Sys::Linux::Unshare qw/:all/; use POSIX qw/_exit/; use Moo; -use Carp qw/carp/; +use Carp qw/croak/; + +use Data::Dumper; has no_proc => (is => 'rw'); @@ -22,7 +24,7 @@ sub _uflags { my $self = shift; my $uflags = 0; - $uflags |= CLONE_NEWNS if ($self->private_tmp || $self->private_mount); + $uflags |= CLONE_NEWNS if ($self->private_tmp || $self->private_mount || ($self->private_pid && !$self->no_proc)); $uflags |= CLONE_NEWPID if ($self->private_pid); $uflags |= CLONE_NEWNET if ($self->private_net); $uflags |= CLONE_NEWIPC if ($self->private_ipc); @@ -35,11 +37,11 @@ sub _uflags { sub _subprocess { my ($self, $code, %args) = @_; - die "_subprocess requires a CODE ref" unless ref $code eq 'CODE'; + croak "_subprocess requires a CODE ref" unless ref $code eq 'CODE'; my $pid = fork(); - carp "Failed to fork: $!" if ($pid < 0); + croak "Failed to fork: $!" if ($pid < 0); if ($pid) { waitpid($pid, 0); # block and wait on child return $?; @@ -52,16 +54,17 @@ sub _subprocess { sub pre_setup { my ($self, %args) = @_; - die "Private net is not yet supported" if $self->private_net; + croak "Private net is not yet supported" if $self->private_net; if ($self->private_pid && (ref $args{code} ne 'CODE' || !$args{_run})) { - die "Private PID space requires a coderef to become the new PID 1"; + warn Dumper(\%args); + croak "Private PID space requires a coderef to become the new PID 1"; } } sub post_setup { my ($self, %args) = @_; # If we want a private /tmp, or private mount we need to recursively make every mount private. it CAN be done without that but this is more reliable. - if ($self->private_mount || $self->private_tmp) { + if ($self->private_tmp || $self->private_mount || ($self->private_pid && !$self->no_proc)) { mount("/", "/", undef, MS_REC|MS_PRIVATE, undef); } @@ -69,21 +72,20 @@ sub post_setup { my $data = undef; $data = $self->private_tmp if (ref $self->private_tmp eq 'HASH'); - eval {umount("/tmp")}; # ignore it if it wasn't mounted - mount("/tmp", "/tmp", "tmpfs", 0, undef); - mount("/tmp", "/tmp", "tmpfs", MS_PRIVATE, $data); + mount("none", "/tmp", "tmpfs", 0, undef); + mount("none", "/tmp", "tmpfs", MS_PRIVATE, $data); } if ($self->private_pid && !$self->no_proc) { - eval {umount("/proc")}; # ignore it if it wasn't mounted already - mount("/proc", "/proc", "proc", 0, undef); - mount("/proc", "/proc", "proc", MS_PRIVATE, undef); + mount("proc", "/proc", "proc", MS_MGC_VAL, undef); + mount("proc", "/proc", "proc", MS_PRIVATE|MS_REC, undef); } } sub setup { my ($self, %args) = @_; + warn Dumper(\%args); my $uflags = $self->_uflags; $self->pre_setup(%args); @@ -99,9 +101,7 @@ sub run { my $code = $args{code}; $args{_run} = 1; - carp "Run must be given a codref to run" unless ref $code eq "CODE"; - my $uflags = $self->_uflags; - $self->pre_setup(%args); + croak "Run must be given a codref to run" unless ref $code eq "CODE"; $self->_subprocess(sub { $self->setup(%args); diff --git a/lib/Sys/Linux/Unshare.pm b/lib/Sys/Linux/Unshare.pm index e4b56ae..a1b48d5 100644 --- a/lib/Sys/Linux/Unshare.pm +++ b/lib/Sys/Linux/Unshare.pm @@ -5,6 +5,7 @@ use warnings; use Data::Dumper; require Exporter; our @ISA = qw/Exporter/; +use Carp qw/croak/; require XSLoader; @@ -27,7 +28,7 @@ sub unshare { my $ret = _unshare_sys($flags); if ($ret != 0) { - die "unshare failed $ret $!"; + croak "unshare failed $ret $!"; } return; diff --git a/t/02-namespace.t b/t/02-namespace.t index 736b6ed..45e7d32 100644 --- a/t/02-namespace.t +++ b/t/02-namespace.t @@ -16,7 +16,14 @@ SKIP: { my $ret = $namespace->run(code => sub { is_deeply([glob "/tmp/*"], [], "No files present in /tmp"); }); - + + ok(my $pid_ns = Sys::Linux::Namespace->new(private_tmp => 1, private_pid => 1), "Setup pid object"); + + $pid_ns->run(code => sub { + is($$, 1, "We're init"); + is_deeply([grep {m|/proc/\d+/|} glob '/proc/*/'], ['/proc/1/'], "Only /proc/1/ exists"); + }); + ok($ret, "run code in sandbox"); ok($namespace->setup(), "Setup namespace in current process");