mirror of
https://github.com/containers/podman.git
synced 2025-05-17 06:59:07 +08:00

When container is being removed, podman iterates through its exec sessions and checks whether exec session pid is still alive. The problem is that the pid can be reused for other processes, so that it may not belong to exec session. In this scenario podman may kill another process This commit prevents it by doing following changes: - Adds the PIDData string to ExecSession struct. This string is used to store additional context for a PID to later verify that the PID killed by the podman is really the one started by it. - Adds new package called pidhandle which implements the methods generating the PIDData, and killing the PID with the PIDData ensuring the right PID is killed by verifying the metadata. The new code uses pidfd_open and name_to_handle_at when available. It fallbacks to process start-time get using the gopsutil package. Fixes: #25104 Signed-off-by: Jan Kaluza <jkaluza@redhat.com>
236 lines
6.3 KiB
Go
236 lines
6.3 KiB
Go
//go:build linux
|
|
|
|
package pidhandle
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
func TestNewPIDHandle(t *testing.T) {
|
|
// Mock the pidfdOpen
|
|
original := pidfdOpen
|
|
defer func() { pidfdOpen = original }()
|
|
pidfdOpen = func(pid int, flags int) (int, error) {
|
|
return 255, nil
|
|
}
|
|
|
|
// Mock the nameToHandleAt
|
|
original_nameToHandleAt := nameToHandleAt
|
|
defer func() { nameToHandleAt = original_nameToHandleAt }()
|
|
nameToHandleAt = func(dirfd int, path string, flags int) (handle unix.FileHandle, mountID int, err error) {
|
|
return newFileHandle(254, []byte("test")), 1, nil
|
|
}
|
|
|
|
h, err := NewPIDHandle(os.Getpid())
|
|
assert.NoError(t, err)
|
|
defer h.Close()
|
|
|
|
pidData, err := h.String()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, nameToHandlePrefix+"254 74657374", pidData)
|
|
assert.Equal(t, h.PID(), os.Getpid())
|
|
}
|
|
|
|
func TestNewPIDHandlepidfdOpenNotSupported(t *testing.T) {
|
|
// Mock the pidfdOpen
|
|
original := pidfdOpen
|
|
defer func() { pidfdOpen = original }()
|
|
pidfdOpen = func(pid int, flags int) (int, error) {
|
|
return -1, unix.ENOSYS
|
|
}
|
|
|
|
h, err := NewPIDHandle(os.Getpid())
|
|
assert.NoError(t, err)
|
|
defer h.Close()
|
|
|
|
pidData, err := h.String()
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, pidData, startTimePrefix)
|
|
assert.Equal(t, h.PID(), os.Getpid())
|
|
}
|
|
|
|
func TestPIDHandleStringnameToHandleAtNotSupported(t *testing.T) {
|
|
// Mock the pidfdOpen
|
|
original := pidfdOpen
|
|
defer func() { pidfdOpen = original }()
|
|
pidfdOpen = func(pid int, flags int) (int, error) {
|
|
return 254, nil
|
|
}
|
|
|
|
// Mock the nameToHandleAt
|
|
original_nameToHandleAt := nameToHandleAt
|
|
defer func() { nameToHandleAt = original_nameToHandleAt }()
|
|
nameToHandleAt = func(dirfd int, path string, flags int) (handle unix.FileHandle, mountID int, err error) {
|
|
return newFileHandle(-1, []byte("")), 1, unix.ENOTSUP
|
|
}
|
|
|
|
h, err := NewPIDHandle(os.Getpid())
|
|
assert.NoError(t, err)
|
|
defer h.Close()
|
|
|
|
pidData, err := h.String()
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, pidData, startTimePrefix)
|
|
}
|
|
|
|
func TestNewPIDHandleFromString(t *testing.T) {
|
|
// Mock the pidfdOpen
|
|
original := pidfdOpen
|
|
defer func() { pidfdOpen = original }()
|
|
pidfdOpen = func(pid int, flags int) (int, error) {
|
|
return 254, nil
|
|
}
|
|
|
|
// Mock the newFileHandle
|
|
original_newFileHandle := newFileHandle
|
|
defer func() { newFileHandle = original_newFileHandle }()
|
|
newFileHandle = func(fhType int32, bytes []byte) unix.FileHandle {
|
|
return unix.NewFileHandle(254, []byte("test"))
|
|
}
|
|
|
|
// Mock the openByHandleAt
|
|
original_openByHandleAt := openByHandleAt
|
|
defer func() { openByHandleAt = original_openByHandleAt }()
|
|
openByHandleAt = func(mountFD int, handle unix.FileHandle, flags int) (fd int, err error) {
|
|
return 255, nil
|
|
}
|
|
|
|
// Mock the nameToHandleAt
|
|
original_nameToHandleAt := nameToHandleAt
|
|
defer func() { nameToHandleAt = original_nameToHandleAt }()
|
|
nameToHandleAt = func(dirfd int, path string, flags int) (handle unix.FileHandle, mountID int, err error) {
|
|
return newFileHandle(255, []byte("test")), 1, nil
|
|
}
|
|
|
|
h, err := NewPIDHandleFromString(os.Getpid(), nameToHandlePrefix+"254 74657374")
|
|
assert.NoError(t, err)
|
|
defer h.Close()
|
|
|
|
pidData, err := h.String()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, nameToHandlePrefix+"254 74657374", pidData)
|
|
assert.Equal(t, h.PID(), os.Getpid())
|
|
}
|
|
|
|
func TestNewPIDHandleFromStringWrongPidData(t *testing.T) {
|
|
// Mock the pidfdOpen
|
|
original := pidfdOpen
|
|
defer func() { pidfdOpen = original }()
|
|
pidfdOpen = func(pid int, flags int) (int, error) {
|
|
return 254, nil
|
|
}
|
|
|
|
// Mock the newFileHandle
|
|
original_newFileHandle := newFileHandle
|
|
defer func() { newFileHandle = original_newFileHandle }()
|
|
newFileHandle = func(fhType int32, bytes []byte) unix.FileHandle {
|
|
return unix.NewFileHandle(254, []byte("test"))
|
|
}
|
|
|
|
// Mock the openByHandleAt
|
|
original_openByHandleAt := openByHandleAt
|
|
defer func() { openByHandleAt = original_openByHandleAt }()
|
|
openByHandleAt = func(mountFD int, handle unix.FileHandle, flags int) (fd int, err error) {
|
|
return 255, nil
|
|
}
|
|
|
|
values := []string{
|
|
nameToHandlePrefix + "foo",
|
|
nameToHandlePrefix + "254 foo",
|
|
nameToHandlePrefix + "254 foo bar",
|
|
nameToHandlePrefix + "foo 1245",
|
|
}
|
|
|
|
for _, s := range values {
|
|
_, err := NewPIDHandleFromString(os.Getpid(), s)
|
|
assert.Error(t, err)
|
|
}
|
|
}
|
|
|
|
func TestPIDHandlePidfdStartTime(t *testing.T) {
|
|
h, err := NewPIDHandleFromString(os.Getpid(), "start-time:1234567890")
|
|
assert.NoError(t, err)
|
|
defer h.Close()
|
|
}
|
|
|
|
func TestPIDHandleKill(t *testing.T) {
|
|
// Mock the pidfdOpen
|
|
original := pidfdOpen
|
|
defer func() { pidfdOpen = original }()
|
|
pidfdOpen = func(pid int, flags int) (int, error) {
|
|
return 254, nil
|
|
}
|
|
|
|
// Mock the pidfdSendSignal
|
|
original_pidfdSendSignal := pidfdSendSignal
|
|
defer func() { pidfdSendSignal = original_pidfdSendSignal }()
|
|
pidfdSendSignal = func(pidfd int, sig unix.Signal, info *unix.Siginfo, flags int) (err error) {
|
|
return unix.ESRCH
|
|
}
|
|
|
|
// Mock the nameToHandleAt
|
|
original_nameToHandleAt := nameToHandleAt
|
|
defer func() { nameToHandleAt = original_nameToHandleAt }()
|
|
nameToHandleAt = func(dirfd int, path string, flags int) (handle unix.FileHandle, mountID int, err error) {
|
|
return newFileHandle(254, []byte("test")), 1, nil
|
|
}
|
|
|
|
h, err := NewPIDHandle(os.Getpid())
|
|
assert.NoError(t, err)
|
|
defer h.Close()
|
|
|
|
err = h.Kill(0)
|
|
assert.ErrorIs(t, err, unix.ESRCH)
|
|
}
|
|
|
|
func TestPIDHandleKillPidfdNotSupported(t *testing.T) {
|
|
// Mock the pidfdOpen
|
|
original := pidfdOpen
|
|
defer func() { pidfdOpen = original }()
|
|
pidfdOpen = func(pid int, flags int) (int, error) {
|
|
return -1, unix.ENOSYS
|
|
}
|
|
|
|
h, err := NewPIDHandle(os.Getpid())
|
|
assert.NoError(t, err)
|
|
defer h.Close()
|
|
|
|
pidData, err := h.String()
|
|
assert.NoError(t, err)
|
|
h, err = NewPIDHandleFromString(os.Getpid(), pidData)
|
|
assert.NoError(t, err)
|
|
|
|
err = h.Kill(0)
|
|
assert.NoError(t, err)
|
|
isAlive, err := h.IsAlive()
|
|
assert.NoError(t, err)
|
|
assert.True(t, isAlive)
|
|
}
|
|
|
|
func TestPIDHandleKillPidfdNotSupportedStartTimeNotMatch(t *testing.T) {
|
|
// Mock the pidfdOpen
|
|
original := pidfdOpen
|
|
defer func() { pidfdOpen = original }()
|
|
pidfdOpen = func(pid int, flags int) (int, error) {
|
|
return -1, unix.ENOSYS
|
|
}
|
|
|
|
h, err := NewPIDHandle(os.Getpid())
|
|
assert.NoError(t, err)
|
|
defer h.Close()
|
|
|
|
h, err = NewPIDHandleFromString(os.Getpid(), "start-time:1234567890")
|
|
assert.NoError(t, err)
|
|
|
|
err = h.Kill(0)
|
|
assert.ErrorIs(t, err, unix.ESRCH)
|
|
|
|
isAlive, err := h.IsAlive()
|
|
assert.NoError(t, err)
|
|
assert.False(t, isAlive)
|
|
}
|