From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by finch.gentoo.org (Postfix) with ESMTPS id 02F35158041 for ; Sat, 24 Feb 2024 20:10:59 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id 08AAAE29B5; Sat, 24 Feb 2024 20:10:58 +0000 (UTC) Received: from smtp.gentoo.org (dev.gentoo.org [IPv6:2001:470:ea4a:1:5054:ff:fec7:86e4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id D94A3E29B5 for ; Sat, 24 Feb 2024 20:10:57 +0000 (UTC) Received: from oystercatcher.gentoo.org (oystercatcher.gentoo.org [148.251.78.52]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id 0849D343052 for ; Sat, 24 Feb 2024 20:10:57 +0000 (UTC) Received: from localhost.localdomain (localhost [IPv6:::1]) by oystercatcher.gentoo.org (Postfix) with ESMTP id 6D1C3131B for ; Sat, 24 Feb 2024 20:10:55 +0000 (UTC) From: "Zac Medico" To: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: 8bit Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "Zac Medico" Message-ID: <1708805259.3f4250dc7d32e9915224b1c9c4bc04c2740abcda.zmedico@gentoo> Subject: [gentoo-commits] proj/portage:master commit in: lib/portage/ X-VCS-Repository: proj/portage X-VCS-Files: lib/portage/process.py X-VCS-Directories: lib/portage/ X-VCS-Committer: zmedico X-VCS-Committer-Name: Zac Medico X-VCS-Revision: 3f4250dc7d32e9915224b1c9c4bc04c2740abcda X-VCS-Branch: master Date: Sat, 24 Feb 2024 20:10:55 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply X-Archives-Salt: 181e89de-2943-4b8e-bb1a-bb0c6ef96298 X-Archives-Hash: e88f6b3f4790a0f545ca5c87f45b1c86 commit: 3f4250dc7d32e9915224b1c9c4bc04c2740abcda Author: Zac Medico gentoo org> AuthorDate: Fri Feb 23 20:35:04 2024 +0000 Commit: Zac Medico gentoo org> CommitDate: Sat Feb 24 20:07:39 2024 +0000 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=3f4250dc process.spawn: Fix logic for missing libc.unshare on musl Fix unshare_* variables to be False when the libc is missing, libc.unshare is missing, or libc.unshare fails. Also, if socket.sethostname is missing then _exec2 needs libc for the network-sandbox sethostname call which is wrapped by a blanket Exception handler. Fixes: 419cce79f908 ("process._exec: Use _start_fork for os.fork() error handling") Bug: https://bugs.gentoo.org/925311 Signed-off-by: Zac Medico gentoo.org> lib/portage/process.py | 213 +++++++++++++++++++++++++------------------------ 1 file changed, 110 insertions(+), 103 deletions(-) diff --git a/lib/portage/process.py b/lib/portage/process.py index f4758c824c..d16262e75a 100644 --- a/lib/portage/process.py +++ b/lib/portage/process.py @@ -956,114 +956,119 @@ def _exec( signal.signal(signal.SIGQUIT, signal.SIG_DFL) # Unshare (while still uid==0) + have_unshare = False + libc = None if unshare_net or unshare_ipc or unshare_mount or unshare_pid: filename = find_library("c") if filename is not None: libc = LoadLibrary(filename) if libc is not None: - # unshare() may not be supported by libc - if not hasattr(libc, "unshare"): - unshare_net = False - unshare_ipc = False - unshare_mount = False - unshare_pid = False - else: - # Since a failed unshare call could corrupt process - # state, first validate that the call can succeed. - # The parent process should call _unshare_validate - # before it forks, so that all child processes can - # reuse _unshare_validate results that have been - # cached by the parent process. - errno_value = _unshare_validate(unshare_flags) - if errno_value == 0 and libc.unshare(unshare_flags) != 0: - errno_value = ctypes.get_errno() - if errno_value != 0: - involved_features = [] - if unshare_ipc: - involved_features.append("ipc-sandbox") - if unshare_mount: - involved_features.append("mount-sandbox") - if unshare_net: - involved_features.append("network-sandbox") - if unshare_pid: - involved_features.append("pid-sandbox") - - writemsg( - 'Unable to unshare: %s (for FEATURES="%s")\n' - % ( - errno.errorcode.get(errno_value, "?"), - " ".join(involved_features), - ), - noiselevel=-1, - ) - else: - if unshare_pid: - # pid namespace requires us to become init - binary, myargs = ( - portage._python_interpreter, - [ - portage._python_interpreter, - os.path.join(portage._bin_path, "pid-ns-init"), - _unicode_encode("" if uid is None else str(uid)), - _unicode_encode("" if gid is None else str(gid)), - _unicode_encode( - "" - if groups is None - else ",".join(str(group) for group in groups) - ), - _unicode_encode( - "" if umask is None else str(umask) - ), - _unicode_encode( - ",".join(str(fd) for fd in fd_pipes) - ), - binary, - ] - + myargs, - ) - uid = None - gid = None - groups = None - umask = None - - # Use _start_fork for os.fork() error handling, ensuring - # that if exec fails then the child process will display - # a traceback before it exits via os._exit to suppress any - # finally blocks from parent's call stack (bug 345289). - main_child_pid = _start_fork( - _exec2, - args=( - binary, - myargs, - env, - gid, - groups, - uid, - umask, - cwd, - pre_exec, - unshare_net, - unshare_ipc, - unshare_mount, - unshare_pid, - ), - fd_pipes=None, - close_fds=False, - ) - - # 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"), - str(main_child_pid), - ] - - os.execve(binary, myargs, env) + have_unshare = hasattr(libc, "unshare") + + if not have_unshare: + # unshare() may not be supported by libc + unshare_net = False + unshare_ipc = False + unshare_mount = False + unshare_pid = False + + if unshare_net or unshare_ipc or unshare_mount or unshare_pid: + # Since a failed unshare call could corrupt process + # state, first validate that the call can succeed. + # The parent process should call _unshare_validate + # before it forks, so that all child processes can + # reuse _unshare_validate results that have been + # cached by the parent process. + errno_value = _unshare_validate(unshare_flags) + if errno_value == 0 and libc.unshare(unshare_flags) != 0: + errno_value = ctypes.get_errno() + if errno_value != 0: + involved_features = [] + if unshare_ipc: + involved_features.append("ipc-sandbox") + if unshare_mount: + involved_features.append("mount-sandbox") + if unshare_net: + involved_features.append("network-sandbox") + if unshare_pid: + involved_features.append("pid-sandbox") + + writemsg( + 'Unable to unshare: %s (for FEATURES="%s")\n' + % ( + errno.errorcode.get(errno_value, "?"), + " ".join(involved_features), + ), + noiselevel=-1, + ) + + unshare_net = False + unshare_ipc = False + unshare_mount = False + unshare_pid = False + + if unshare_pid: + # pid namespace requires us to become init + binary, myargs = ( + portage._python_interpreter, + [ + portage._python_interpreter, + os.path.join(portage._bin_path, "pid-ns-init"), + _unicode_encode("" if uid is None else str(uid)), + _unicode_encode("" if gid is None else str(gid)), + _unicode_encode( + "" if groups is None else ",".join(str(group) for group in groups) + ), + _unicode_encode("" if umask is None else str(umask)), + _unicode_encode(",".join(str(fd) for fd in fd_pipes)), + binary, + ] + + myargs, + ) + uid = None + gid = None + groups = None + umask = None + + # Use _start_fork for os.fork() error handling, ensuring + # that if exec fails then the child process will display + # a traceback before it exits via os._exit to suppress any + # finally blocks from parent's call stack (bug 345289). + main_child_pid = _start_fork( + _exec2, + args=( + binary, + myargs, + env, + gid, + groups, + uid, + umask, + cwd, + pre_exec, + unshare_net, + unshare_ipc, + unshare_mount, + unshare_pid, + libc, + ), + fd_pipes=None, + close_fds=False, + ) + + # 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"), + str(main_child_pid), + ] + + os.execve(binary, myargs, env) # Reachable only if unshare_pid is False. _exec2( @@ -1080,6 +1085,7 @@ def _exec( unshare_ipc, unshare_mount, unshare_pid, + libc, ) @@ -1097,6 +1103,7 @@ def _exec2( unshare_ipc, unshare_mount, unshare_pid, + libc, ): if unshare_mount: # mark the whole filesystem as slave to avoid