exec: do not leak session IDs on errors

always cleanup the exec session when the command specified to the
"exec" is not found.

Closes: https://github.com/containers/podman/issues/20392

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
This commit is contained in:
Giuseppe Scrivano 2023-10-18 10:09:56 +02:00
parent 64171043ac
commit fa19e1baa2
No known key found for this signature in database
GPG Key ID: 67E38F7A8BA21772
3 changed files with 24 additions and 10 deletions

View File

@ -759,11 +759,19 @@ func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resi
// Exec emulates the old Libpod exec API, providing a single call to create, // Exec emulates the old Libpod exec API, providing a single call to create,
// run, and remove an exec session. Returns exit code and error. Exit code is // run, and remove an exec session. Returns exit code and error. Exit code is
// not guaranteed to be set sanely if error is not nil. // not guaranteed to be set sanely if error is not nil.
func (c *Container) exec(config *ExecConfig, streams *define.AttachStreams, resizeChan <-chan resize.TerminalSize, isHealthcheck bool) (int, error) { func (c *Container) exec(config *ExecConfig, streams *define.AttachStreams, resizeChan <-chan resize.TerminalSize, isHealthcheck bool) (exitCode int, retErr error) {
sessionID, err := c.ExecCreate(config) sessionID, err := c.ExecCreate(config)
if err != nil { if err != nil {
return -1, err return -1, err
} }
defer func() {
if err := c.ExecRemove(sessionID, false); err != nil {
if retErr == nil && !errors.Is(err, define.ErrNoSuchExecSession) {
exitCode = -1
retErr = err
}
}
}()
// Start resizing if we have a resize channel. // Start resizing if we have a resize channel.
// This goroutine may likely leak, given that we cannot close it here. // This goroutine may likely leak, given that we cannot close it here.
@ -813,15 +821,7 @@ func (c *Container) exec(config *ExecConfig, streams *define.AttachStreams, resi
} }
return -1, err return -1, err
} }
exitCode := session.ExitCode return session.ExitCode, nil
if err := c.ExecRemove(sessionID, false); err != nil {
if errors.Is(err, define.ErrNoSuchExecSession) {
return exitCode, nil
}
return -1, err
}
return exitCode, nil
} }
// cleanupExecBundle cleanups an exec session after its done // cleanupExecBundle cleanups an exec session after its done

View File

@ -904,6 +904,7 @@ func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID s
// TODO: we should try and retrieve exit code if this fails. // TODO: we should try and retrieve exit code if this fails.
if err := ctr.ExecStart(id); err != nil { if err := ctr.ExecStart(id); err != nil {
_ = ctr.ExecRemove(id, true)
return "", err return "", err
} }
return id, nil return id, nil

View File

@ -166,4 +166,17 @@ load helpers
run_podman rm -f -t0 $cid run_podman rm -f -t0 $cid
} }
@test "podman exec - does not leak session IDs on invalid command" {
run_podman run -d $IMAGE top
cid="$output"
for i in {1..3}; do
run_podman 127 exec $cid blahblah
run_podman 125 exec -d $cid blahblah
done
run_podman inspect --format "{{len .ExecIDs}}" $cid
assert "$output" = "0" ".ExecIDs must be empty"
}
# vim: filetype=sh # vim: filetype=sh