From 2a7dcbbb4039f51c21b2051124ff2ac40a785b46 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Fri, 12 May 2023 16:04:11 -0700 Subject: [PATCH] libct: fix shared pidns detection When someone is using libcontainer to start and kill containers from a long lived process (i.e. the same process creates and removes the container), initProcess.wait method is used, which has a kludge to work around killing containers that do not have their own PID namespace. The code that checks for own PID namespace is not entirely correct. To be exact, it does not set sharePidns flag when the host/caller PID namespace is implicitly used. As a result, the above mentioned kludge does not work. Fix the issue, add a test case (which fails without the fix). Signed-off-by: Kir Kolyshkin --- libcontainer/configs/namespaces_syscall.go | 12 ++++++++++++ libcontainer/container_linux.go | 3 +-- libcontainer/integration/exec_test.go | 20 +++++++++++++++----- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/libcontainer/configs/namespaces_syscall.go b/libcontainer/configs/namespaces_syscall.go index 0516dba8d..543e059aa 100644 --- a/libcontainer/configs/namespaces_syscall.go +++ b/libcontainer/configs/namespaces_syscall.go @@ -31,3 +31,15 @@ func (n *Namespaces) CloneFlags() uintptr { } return uintptr(flag) } + +// IsPrivate tells whether the namespace of type t is configured as private +// (i.e. it exists and is not shared). +func (n Namespaces) IsPrivate(t NamespaceType) bool { + for _, v := range n { + if v.Type == t { + return v.Path == "" + } + } + // Not found, so implicitly sharing a parent namespace. + return false +} diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go index 73264bc90..59262d3ab 100644 --- a/libcontainer/container_linux.go +++ b/libcontainer/container_linux.go @@ -549,7 +549,6 @@ func (c *Container) newInitProcess(p *Process, cmd *exec.Cmd, messageSockPair, l nsMaps[ns.Type] = ns.Path } } - _, sharePidns := nsMaps[configs.NEWPID] data, err := c.bootstrapData(c.config.Namespaces.CloneFlags(), nsMaps, initStandard) if err != nil { return nil, err @@ -594,7 +593,7 @@ func (c *Container) newInitProcess(p *Process, cmd *exec.Cmd, messageSockPair, l container: c, process: p, bootstrapData: data, - sharePidns: sharePidns, + sharePidns: !c.config.Namespaces.IsPrivate(configs.NEWPID), } c.initProcess = init return init, nil diff --git a/libcontainer/integration/exec_test.go b/libcontainer/integration/exec_test.go index 376dd0f1f..4ac0f466b 100644 --- a/libcontainer/integration/exec_test.go +++ b/libcontainer/integration/exec_test.go @@ -1355,16 +1355,26 @@ func TestPIDHost(t *testing.T) { } } -func TestPIDHostInitProcessWait(t *testing.T) { +func TestHostPidnsInitKill(t *testing.T) { + config := newTemplateConfig(t, nil) + // Implicitly use host pid ns. + config.Namespaces.Remove(configs.NEWPID) + testPidnsInitKill(t, config) +} + +func TestSharedPidnsInitKill(t *testing.T) { + config := newTemplateConfig(t, nil) + // Explicitly use host pid ns. + config.Namespaces.Add(configs.NEWPID, "/proc/1/ns/pid") + testPidnsInitKill(t, config) +} + +func testPidnsInitKill(t *testing.T, config *configs.Config) { if testing.Short() { return } - pidns := "/proc/1/ns/pid" - // Run a container with two long-running processes. - config := newTemplateConfig(t, nil) - config.Namespaces.Add(configs.NEWPID, pidns) container, err := newContainer(t, config) ok(t, err) defer func() {