public inbox for gentoo-portage-dev@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-portage-dev] [PATCH] pid-sandbox: execute pid-ns-init as pid 1 (bug 675312)
@ 2019-01-14  0:27 Zac Medico
  2019-01-14  4:57 ` Brian Dolbec
  0 siblings, 1 reply; 3+ messages in thread
From: Zac Medico @ 2019-01-14  0:27 UTC (permalink / raw
  To: gentoo-portage-dev; +Cc: Zac Medico

Execute pid-ns-init as the first fork after unshare, as
required for it to have pid 1 and become the default reaper
of orphaned descendant processes. In _exec, exec a separate
pid-ns-init process to behave as a supervisor which will
forward signals to init and forward exit status to the parent
process.

Fixes: a75d5546e3a4 ("Introduce a tiny init replacement for inside pid namespace")
Bug: https://bugs.gentoo.org/670484
---
 bin/pid-ns-init        | 44 ++++++++++++++++++++++++++++++++++++++----
 lib/portage/process.py | 26 ++++++++++++++++++-------
 2 files changed, 59 insertions(+), 11 deletions(-)

diff --git a/bin/pid-ns-init b/bin/pid-ns-init
index 843257b70..3792eeaa4 100644
--- a/bin/pid-ns-init
+++ b/bin/pid-ns-init
@@ -1,23 +1,59 @@
 #!/usr/bin/env python
-# Copyright 2018 Gentoo Authors
+# Copyright 2018-2019 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
+import functools
 import os
+import signal
 import sys
 
 
+KILL_SIGNALS = (
+	signal.SIGINT,
+	signal.SIGTERM,
+	signal.SIGHUP,
+)
+
+def forward_kill_signal(main_child_pid, signum, frame):
+	os.kill(main_child_pid, signum)
+
+
 def main(argv):
 	if len(argv) < 2:
-		return 'Usage: {} <main-child-pid>'.format(argv[0])
-	main_child_pid = int(argv[1])
+		return 'Usage: {} <main-child-pid> or <binary> argv0..'.format(argv[0])
+
+	if len(argv) == 2:
+		# The child process is init (pid 1) in a child pid namespace, and
+		# the current process supervises from within the global pid namespace
+		# (forwarding signals to init and forwarding exit status to the parent
+		# process).
+		main_child_pid = int(argv[1])
+	else:
+		# The current process is init (pid 1) in a child pid namespace.
+		binary = argv[1]
+		args = argv[2:]
+
+		main_child_pid = os.fork()
+		if main_child_pid == 0:
+			os.execv(binary, args)
+
+	sig_handler = functools.partial(forward_kill_signal, main_child_pid)
+	for signum in KILL_SIGNALS:
+		signal.signal(signum, sig_handler)
 
 	# wait for child processes
 	while True:
-		pid, status = os.wait()
+		try:
+			pid, status = os.wait()
+		except OSError as e:
+			if e.errno == errno.EINTR:
+				continue
+			raise
 		if pid == main_child_pid:
 			if os.WIFEXITED(status):
 				return os.WEXITSTATUS(status)
 			elif os.WIFSIGNALED(status):
+				signal.signal(os.WTERMSIG(status), signal.SIG_DFL)
 				os.kill(os.getpid(), os.WTERMSIG(status))
 			# go to the unreachable place
 			break
diff --git a/lib/portage/process.py b/lib/portage/process.py
index 7103b6b31..3e07f806c 100644
--- a/lib/portage/process.py
+++ b/lib/portage/process.py
@@ -564,15 +564,27 @@ def _exec(binary, mycommand, opt_name, fd_pipes,
 							noiselevel=-1)
 					else:
 						if unshare_pid:
-							# pid namespace requires us to become init
-							fork_ret = os.fork()
-							if fork_ret != 0:
-								os.execv(portage._python_interpreter, [
+							main_child_pid = os.fork()
+							if main_child_pid == 0:
+								# pid namespace requires us to become init
+								binary, myargs = portage._python_interpreter, [
+										portage._python_interpreter,
+										os.path.join(portage._bin_path,
+											'pid-ns-init')] + [binary] + myargs
+							else:
+								# Execute a supervisor process which will forward
+								# signals to init and forward exit status to the
+								# parent process. The supervisor process runs in
+								# the global pid namespace, so skip /proc remount
+								# and other setup that's intended only for the
+								# init process.
+								binary, myargs = portage._python_interpreter, [
 									portage._python_interpreter,
 									os.path.join(portage._bin_path,
-										'pid-ns-init'),
-									'%s' % fork_ret,
-									])
+									'pid-ns-init'), str(main_child_pid)]
+
+								os.execve(binary, myargs, env)
+
 						if unshare_mount:
 							# mark the whole filesystem as slave to avoid
 							# mounts escaping the namespace
-- 
2.18.1



^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [gentoo-portage-dev] [PATCH] pid-sandbox: execute pid-ns-init as pid 1 (bug 675312)
  2019-01-14  0:27 [gentoo-portage-dev] [PATCH] pid-sandbox: execute pid-ns-init as pid 1 (bug 675312) Zac Medico
@ 2019-01-14  4:57 ` Brian Dolbec
  2019-01-15  6:37   ` Zac Medico
  0 siblings, 1 reply; 3+ messages in thread
From: Brian Dolbec @ 2019-01-14  4:57 UTC (permalink / raw
  To: gentoo-portage-dev

On Sun, 13 Jan 2019 16:27:21 -0800
Zac Medico <zmedico@gentoo.org> wrote:

> Execute pid-ns-init as the first fork after unshare, as
> required for it to have pid 1 and become the default reaper
> of orphaned descendant processes. In _exec, exec a separate
> pid-ns-init process to behave as a supervisor which will
> forward signals to init and forward exit status to the parent
> process.
> 
> Fixes: a75d5546e3a4 ("Introduce a tiny init replacement for inside
> pid namespace") Bug: https://bugs.gentoo.org/670484
> ---
>  bin/pid-ns-init        | 44
> ++++++++++++++++++++++++++++++++++++++---- lib/portage/process.py |
> 26 ++++++++++++++++++------- 2 files changed, 59 insertions(+), 11
> deletions(-)
> 
> diff --git a/bin/pid-ns-init b/bin/pid-ns-init
> index 843257b70..3792eeaa4 100644
> --- a/bin/pid-ns-init
> +++ b/bin/pid-ns-init
> @@ -1,23 +1,59 @@
>  #!/usr/bin/env python
> -# Copyright 2018 Gentoo Authors
> +# Copyright 2018-2019 Gentoo Authors
>  # Distributed under the terms of the GNU General Public License v2
>  
> +import functools
>  import os
> +import signal
>  import sys
>  
>  
> +KILL_SIGNALS = (
> +	signal.SIGINT,
> +	signal.SIGTERM,
> +	signal.SIGHUP,
> +)
> +
> +def forward_kill_signal(main_child_pid, signum, frame):
> +	os.kill(main_child_pid, signum)
> +
> +
>  def main(argv):
>  	if len(argv) < 2:
> -		return 'Usage: {} <main-child-pid>'.format(argv[0])
> -	main_child_pid = int(argv[1])
> +		return 'Usage: {} <main-child-pid> or <binary>
> argv0..'.format(argv[0]) +
> +	if len(argv) == 2:
> +		# The child process is init (pid 1) in a child pid
> namespace, and
> +		# the current process supervises from within the
> global pid namespace
> +		# (forwarding signals to init and forwarding exit
> status to the parent
> +		# process).
> +		main_child_pid = int(argv[1])
> +	else:
> +		# The current process is init (pid 1) in a child pid
> namespace.
> +		binary = argv[1]
> +		args = argv[2:]
> +
> +		main_child_pid = os.fork()
> +		if main_child_pid == 0:
> +			os.execv(binary, args)
> +
> +	sig_handler = functools.partial(forward_kill_signal,
> main_child_pid)
> +	for signum in KILL_SIGNALS:
> +		signal.signal(signum, sig_handler)
>  
>  	# wait for child processes
>  	while True:
> -		pid, status = os.wait()
> +		try:
> +			pid, status = os.wait()
> +		except OSError as e:
> +			if e.errno == errno.EINTR:
> +				continue
> +			raise
>  		if pid == main_child_pid:
>  			if os.WIFEXITED(status):
>  				return os.WEXITSTATUS(status)
>  			elif os.WIFSIGNALED(status):
> +				signal.signal(os.WTERMSIG(status),
> signal.SIG_DFL) os.kill(os.getpid(), os.WTERMSIG(status))
>  			# go to the unreachable place
>  			break
> diff --git a/lib/portage/process.py b/lib/portage/process.py
> index 7103b6b31..3e07f806c 100644
> --- a/lib/portage/process.py
> +++ b/lib/portage/process.py
> @@ -564,15 +564,27 @@ def _exec(binary, mycommand, opt_name, fd_pipes,
>  							noiselevel=-1)
>  					else:
>  						if unshare_pid:
> -							# pid
> namespace requires us to become init
> -							fork_ret =
> os.fork()
> -							if
> fork_ret != 0:
> -
> os.execv(portage._python_interpreter, [
> +
> main_child_pid = os.fork()
> +							if
> main_child_pid == 0:
> +								#
> pid namespace requires us to become init
> +
> binary, myargs = portage._python_interpreter, [
> +
> portage._python_interpreter,
> +
> os.path.join(portage._bin_path,
> +											'pid-ns-init')]
> + [binary] + myargs
> +							else:
> +								#
> Execute a supervisor process which will forward
> +								#
> signals to init and forward exit status to the
> +								#
> parent process. The supervisor process runs in
> +								#
> the global pid namespace, so skip /proc remount
> +								#
> and other setup that's intended only for the
> +								#
> init process.
> +
> binary, myargs = portage._python_interpreter,
> [ portage._python_interpreter, os.path.join(portage._bin_path,
> -										'pid-ns-init'),
> -									'%s'
> % fork_ret,
> -									])
> +									'pid-ns-init'),
> str(main_child_pid)] +
> +
> os.execve(binary, myargs, env) +
>  						if unshare_mount:
>  							# mark the
> whole filesystem as slave to avoid # mounts escaping the namespace

Looks fine, but I defer to floppym, mgorny since this affects systemd


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [gentoo-portage-dev] [PATCH] pid-sandbox: execute pid-ns-init as pid 1 (bug 675312)
  2019-01-14  4:57 ` Brian Dolbec
@ 2019-01-15  6:37   ` Zac Medico
  0 siblings, 0 replies; 3+ messages in thread
From: Zac Medico @ 2019-01-15  6:37 UTC (permalink / raw
  To: gentoo-portage-dev, Brian Dolbec


[-- Attachment #1.1: Type: text/plain, Size: 4971 bytes --]

On 1/13/19 8:57 PM, Brian Dolbec wrote:
> On Sun, 13 Jan 2019 16:27:21 -0800
> Zac Medico <zmedico@gentoo.org> wrote:
> 
>> Execute pid-ns-init as the first fork after unshare, as
>> required for it to have pid 1 and become the default reaper
>> of orphaned descendant processes. In _exec, exec a separate
>> pid-ns-init process to behave as a supervisor which will
>> forward signals to init and forward exit status to the parent
>> process.
>>
>> Fixes: a75d5546e3a4 ("Introduce a tiny init replacement for inside
>> pid namespace") Bug: https://bugs.gentoo.org/670484
>> ---
>>  bin/pid-ns-init        | 44
>> ++++++++++++++++++++++++++++++++++++++---- lib/portage/process.py |
>> 26 ++++++++++++++++++------- 2 files changed, 59 insertions(+), 11
>> deletions(-)
>>
>> diff --git a/bin/pid-ns-init b/bin/pid-ns-init
>> index 843257b70..3792eeaa4 100644
>> --- a/bin/pid-ns-init
>> +++ b/bin/pid-ns-init
>> @@ -1,23 +1,59 @@
>>  #!/usr/bin/env python
>> -# Copyright 2018 Gentoo Authors
>> +# Copyright 2018-2019 Gentoo Authors
>>  # Distributed under the terms of the GNU General Public License v2
>>  
>> +import functools
>>  import os
>> +import signal
>>  import sys
>>  
>>  
>> +KILL_SIGNALS = (
>> +	signal.SIGINT,
>> +	signal.SIGTERM,
>> +	signal.SIGHUP,
>> +)
>> +
>> +def forward_kill_signal(main_child_pid, signum, frame):
>> +	os.kill(main_child_pid, signum)
>> +
>> +
>>  def main(argv):
>>  	if len(argv) < 2:
>> -		return 'Usage: {} <main-child-pid>'.format(argv[0])
>> -	main_child_pid = int(argv[1])
>> +		return 'Usage: {} <main-child-pid> or <binary>
>> argv0..'.format(argv[0]) +
>> +	if len(argv) == 2:
>> +		# The child process is init (pid 1) in a child pid
>> namespace, and
>> +		# the current process supervises from within the
>> global pid namespace
>> +		# (forwarding signals to init and forwarding exit
>> status to the parent
>> +		# process).
>> +		main_child_pid = int(argv[1])
>> +	else:
>> +		# The current process is init (pid 1) in a child pid
>> namespace.
>> +		binary = argv[1]
>> +		args = argv[2:]
>> +
>> +		main_child_pid = os.fork()
>> +		if main_child_pid == 0:
>> +			os.execv(binary, args)
>> +
>> +	sig_handler = functools.partial(forward_kill_signal,
>> main_child_pid)
>> +	for signum in KILL_SIGNALS:
>> +		signal.signal(signum, sig_handler)
>>  
>>  	# wait for child processes
>>  	while True:
>> -		pid, status = os.wait()
>> +		try:
>> +			pid, status = os.wait()
>> +		except OSError as e:
>> +			if e.errno == errno.EINTR:
>> +				continue
>> +			raise
>>  		if pid == main_child_pid:
>>  			if os.WIFEXITED(status):
>>  				return os.WEXITSTATUS(status)
>>  			elif os.WIFSIGNALED(status):
>> +				signal.signal(os.WTERMSIG(status),
>> signal.SIG_DFL) os.kill(os.getpid(), os.WTERMSIG(status))
>>  			# go to the unreachable place
>>  			break
>> diff --git a/lib/portage/process.py b/lib/portage/process.py
>> index 7103b6b31..3e07f806c 100644
>> --- a/lib/portage/process.py
>> +++ b/lib/portage/process.py
>> @@ -564,15 +564,27 @@ def _exec(binary, mycommand, opt_name, fd_pipes,
>>  							noiselevel=-1)
>>  					else:
>>  						if unshare_pid:
>> -							# pid
>> namespace requires us to become init
>> -							fork_ret =
>> os.fork()
>> -							if
>> fork_ret != 0:
>> -
>> os.execv(portage._python_interpreter, [
>> +
>> main_child_pid = os.fork()
>> +							if
>> main_child_pid == 0:
>> +								#
>> pid namespace requires us to become init
>> +
>> binary, myargs = portage._python_interpreter, [
>> +
>> portage._python_interpreter,
>> +
>> os.path.join(portage._bin_path,
>> +											'pid-ns-init')]
>> + [binary] + myargs
>> +							else:
>> +								#
>> Execute a supervisor process which will forward
>> +								#
>> signals to init and forward exit status to the
>> +								#
>> parent process. The supervisor process runs in
>> +								#
>> the global pid namespace, so skip /proc remount
>> +								#
>> and other setup that's intended only for the
>> +								#
>> init process.
>> +
>> binary, myargs = portage._python_interpreter,
>> [ portage._python_interpreter, os.path.join(portage._bin_path,
>> -										'pid-ns-init'),
>> -									'%s'
>> % fork_ret,
>> -									])
>> +									'pid-ns-init'),
>> str(main_child_pid)] +
>> +
>> os.execve(binary, myargs, env) +
>>  						if unshare_mount:
>>  							# mark the
>> whole filesystem as slave to avoid # mounts escaping the namespace
> 
> Looks fine,

Thanks, merged:

https://gitweb.gentoo.org/proj/portage.git/commit/?id=fb406579b1d13c1ba23b28e0bb794c22878a58c0

> but I defer to floppym, mgorny since this affects systemd

I not experiencing any problems with systemd here. We've got this patch
to solve the systemd pkg_postinst thing:

https://gitweb.gentoo.org/proj/portage.git/commit/?id=bbfc36befdeed60f29c17d80d7766fd0da402d61
-- 
Thanks,
Zac


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 981 bytes --]

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2019-01-15  6:37 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-01-14  0:27 [gentoo-portage-dev] [PATCH] pid-sandbox: execute pid-ns-init as pid 1 (bug 675312) Zac Medico
2019-01-14  4:57 ` Brian Dolbec
2019-01-15  6:37   ` Zac Medico

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox