Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Zac Medico <zmedico@g.o>
Subject: [gentoo-portage-dev] [PATCH] EbuildIpcDaemon: fix lock permission race
Date: Sun, 07 Nov 2021 04:54:59
Message-Id: 20211107045430.152154-1-zmedico@gentoo.org
1 Move ipc files to a .ipc subdirectory, with a setgid bit to
2 prevent a lockfile group permission race. The lockfile function
3 uses an appropriate open call with mode argument so that the
4 lockfile is created atomically with both group ownership and
5 group write bit.
6
7 Bug: https://bugs.gentoo.org/468990
8 Signed-off-by: Zac Medico <zmedico@g.o>
9 ---
10 bin/ebuild-ipc.py | 6 +++---
11 bin/phase-functions.sh | 4 ++--
12 lib/_emerge/AbstractEbuildProcess.py | 4 ++--
13 lib/_emerge/EbuildIpcDaemon.py | 2 +-
14 lib/portage/package/ebuild/prepare_build_dirs.py | 6 ++++++
15 lib/portage/tests/ebuild/test_doebuild_spawn.py | 1 +
16 lib/portage/tests/ebuild/test_ipc_daemon.py | 6 +++---
17 7 files changed, 18 insertions(+), 11 deletions(-)
18
19 diff --git a/bin/ebuild-ipc.py b/bin/ebuild-ipc.py
20 index 4999c043a..c24ba6f58 100755
21 --- a/bin/ebuild-ipc.py
22 +++ b/bin/ebuild-ipc.py
23 @@ -138,9 +138,9 @@ class EbuildIpc:
24
25 def __init__(self):
26 self.fifo_dir = os.environ["PORTAGE_BUILDDIR"]
27 - self.ipc_in_fifo = os.path.join(self.fifo_dir, ".ipc_in")
28 - self.ipc_out_fifo = os.path.join(self.fifo_dir, ".ipc_out")
29 - self.ipc_lock_file = os.path.join(self.fifo_dir, ".ipc_lock")
30 + self.ipc_in_fifo = os.path.join(self.fifo_dir, ".ipc", "in")
31 + self.ipc_out_fifo = os.path.join(self.fifo_dir, ".ipc", "out")
32 + self.ipc_lock_file = os.path.join(self.fifo_dir, ".ipc", "lock")
33
34 def _daemon_is_alive(self):
35 try:
36 diff --git a/bin/phase-functions.sh b/bin/phase-functions.sh
37 index d3221993d..5eb031805 100644
38 --- a/bin/phase-functions.sh
39 +++ b/bin/phase-functions.sh
40 @@ -291,10 +291,10 @@ __dyn_clean() {
41 rm -f "$PORTAGE_BUILDDIR"/.{ebuild_changed,logid,pretended,setuped,unpacked,prepared} \
42 "$PORTAGE_BUILDDIR"/.{configured,compiled,tested,packaged,instprepped} \
43 "$PORTAGE_BUILDDIR"/.die_hooks \
44 - "$PORTAGE_BUILDDIR"/.ipc_{in,out,lock} \
45 "$PORTAGE_BUILDDIR"/.exit_status
46
47 - rm -rf "${PORTAGE_BUILDDIR}/build-info"
48 + rm -rf "${PORTAGE_BUILDDIR}/build-info" \
49 + "${PORTAGE_BUILDDIR}/.ipc"
50 rm -rf "${WORKDIR}"
51 rm -f "${PORTAGE_BUILDDIR}/files"
52 fi
53 diff --git a/lib/_emerge/AbstractEbuildProcess.py b/lib/_emerge/AbstractEbuildProcess.py
54 index 1b4e7759f..6d89d40f0 100644
55 --- a/lib/_emerge/AbstractEbuildProcess.py
56 +++ b/lib/_emerge/AbstractEbuildProcess.py
57 @@ -249,8 +249,8 @@ class AbstractEbuildProcess(SpawnProcess):
58
59 def _init_ipc_fifos(self):
60
61 - input_fifo = os.path.join(self.settings["PORTAGE_BUILDDIR"], ".ipc_in")
62 - output_fifo = os.path.join(self.settings["PORTAGE_BUILDDIR"], ".ipc_out")
63 + input_fifo = os.path.join(self.settings["PORTAGE_BUILDDIR"], ".ipc", "in")
64 + output_fifo = os.path.join(self.settings["PORTAGE_BUILDDIR"], ".ipc", "out")
65
66 for p in (input_fifo, output_fifo):
67
68 diff --git a/lib/_emerge/EbuildIpcDaemon.py b/lib/_emerge/EbuildIpcDaemon.py
69 index ee6fd7658..78594ff0a 100644
70 --- a/lib/_emerge/EbuildIpcDaemon.py
71 +++ b/lib/_emerge/EbuildIpcDaemon.py
72 @@ -81,7 +81,7 @@ class EbuildIpcDaemon(FifoIpcDaemon):
73 # write something to the pipe just before we close it, and in that
74 # case the write will be lost. Therefore, try for a non-blocking
75 # lock, and only re-open the pipe if the lock is acquired.
76 - lock_filename = os.path.join(os.path.dirname(self.input_fifo), ".ipc_lock")
77 + lock_filename = os.path.join(os.path.dirname(self.input_fifo), "lock")
78 try:
79 lock_obj = lockfile(lock_filename, unlinkfile=True, flags=os.O_NONBLOCK)
80 except TryAgain:
81 diff --git a/lib/portage/package/ebuild/prepare_build_dirs.py b/lib/portage/package/ebuild/prepare_build_dirs.py
82 index 659198905..9d2474fd8 100644
83 --- a/lib/portage/package/ebuild/prepare_build_dirs.py
84 +++ b/lib/portage/package/ebuild/prepare_build_dirs.py
85 @@ -102,6 +102,12 @@ def prepare_build_dirs(myroot=None, settings=None, cleanup=False):
86 apply_secpass_permissions(
87 mysettings[dir_key], uid=portage_uid, gid=portage_gid
88 )
89 + # The setgid bit prevents a lockfile group permission race for bug #468990.
90 + ensure_dirs(
91 + os.path.join(mysettings["PORTAGE_BUILDDIR"], ".ipc"),
92 + gid=portage_gid,
93 + mode=0o2770,
94 + )
95 except PermissionDenied as e:
96 writemsg(_("Permission Denied: %s\n") % str(e), noiselevel=-1)
97 return 1
98 diff --git a/lib/portage/tests/ebuild/test_doebuild_spawn.py b/lib/portage/tests/ebuild/test_doebuild_spawn.py
99 index ef0ae5847..d142ec41e 100644
100 --- a/lib/portage/tests/ebuild/test_doebuild_spawn.py
101 +++ b/lib/portage/tests/ebuild/test_doebuild_spawn.py
102 @@ -81,6 +81,7 @@ class DoebuildSpawnTestCase(TestCase):
103 settings["T"] = os.path.join(settings["PORTAGE_BUILDDIR"], "temp")
104 for x in ("PORTAGE_BUILDDIR", "HOME", "T"):
105 os.makedirs(settings[x])
106 + os.makedirs(os.path.join(settings["PORTAGE_BUILDDIR"], ".ipc"))
107 # Create a fake environment, to pretend as if the ebuild
108 # has been sourced already.
109 open(os.path.join(settings["T"], "environment"), "wb").close()
110 diff --git a/lib/portage/tests/ebuild/test_ipc_daemon.py b/lib/portage/tests/ebuild/test_ipc_daemon.py
111 index e20b6fff1..0ac3d3c32 100644
112 --- a/lib/portage/tests/ebuild/test_ipc_daemon.py
113 +++ b/lib/portage/tests/ebuild/test_ipc_daemon.py
114 @@ -66,10 +66,10 @@ class IpcDaemonTestCase(TestCase):
115
116 build_dir = EbuildBuildDir(scheduler=event_loop, settings=env)
117 event_loop.run_until_complete(build_dir.async_lock())
118 - ensure_dirs(env["PORTAGE_BUILDDIR"])
119 + ensure_dirs(os.path.join(env["PORTAGE_BUILDDIR"], ".ipc"))
120
121 - input_fifo = os.path.join(env["PORTAGE_BUILDDIR"], ".ipc_in")
122 - output_fifo = os.path.join(env["PORTAGE_BUILDDIR"], ".ipc_out")
123 + input_fifo = os.path.join(env["PORTAGE_BUILDDIR"], ".ipc", "in")
124 + output_fifo = os.path.join(env["PORTAGE_BUILDDIR"], ".ipc", "out")
125 os.mkfifo(input_fifo)
126 os.mkfifo(output_fifo)
127
128 --
129 2.32.0