runc exec: reject paused container unless --ignore-paused

Currently, if a container is paused (i.e. its cgroup is frozen), runc exec
just hangs, and it is not obvious why.

Refuse to exec in a paused container. Add a test case.

In case runc exec in a paused container is a legit use case,
add --ignore-paused option to override the check. Document it,
add a test case.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This commit is contained in:
Kir Kolyshkin
2021-08-17 16:54:04 -07:00
parent 931eb942aa
commit dd696235a4
4 changed files with 58 additions and 1 deletions
+1
View File
@@ -173,6 +173,7 @@ _runc_exec() {
--apparmor --apparmor
--cap, -c --cap, -c
--preserve-fds --preserve-fds
--ignore-paused
" "
local all_options="$options_with_args $boolean_options" local all_options="$options_with_args $boolean_options"
+8 -1
View File
@@ -91,6 +91,10 @@ following will output a list of processes running in the container:
Name: "cgroup", Name: "cgroup",
Usage: "run the process in an (existing) sub-cgroup(s). Format is [<controller>:]<cgroup>.", Usage: "run the process in an (existing) sub-cgroup(s). Format is [<controller>:]<cgroup>.",
}, },
cli.BoolFlag{
Name: "ignore-paused",
Usage: "allow exec in a paused container",
},
}, },
Action: func(context *cli.Context) error { Action: func(context *cli.Context) error {
if err := checkArgs(context, 1, minArgs); err != nil { if err := checkArgs(context, 1, minArgs); err != nil {
@@ -145,7 +149,10 @@ func execProcess(context *cli.Context) (int, error) {
return -1, err return -1, err
} }
if status == libcontainer.Stopped { if status == libcontainer.Stopped {
return -1, errors.New("cannot exec a container that has stopped") return -1, errors.New("cannot exec in a stopped container")
}
if status == libcontainer.Paused && !context.Bool("ignore-paused") {
return -1, errors.New("cannot exec in a paused container (use --ignore-paused to override)")
} }
path := context.String("process") path := context.String("process")
if path == "" && len(context.Args()) == 1 { if path == "" && len(context.Args()) == 1 {
+5
View File
@@ -59,6 +59,11 @@ multiple times.
: Pass _N_ additional file descriptors to the container (**stdio** + : Pass _N_ additional file descriptors to the container (**stdio** +
**$LISTEN_FDS** + _N_ in total). Default is **0**. **$LISTEN_FDS** + _N_ in total). Default is **0**.
**--ignore-paused**
: Allow exec in a paused container. By default, if a container is paused,
**runc exec** errors out; this option can be used to override it.
A paused container needs to be resumed for the exec to complete.
**--cgroup** _path_ | _controller_[,_controller_...]:_path_ **--cgroup** _path_ | _controller_[,_controller_...]:_path_
: Execute a process in a sub-cgroup. If the specified cgroup does not exist, an : Execute a process in a sub-cgroup. If the specified cgroup does not exist, an
error is returned. Default is empty path, which means to use container's top error is returned. Default is empty path, which means to use container's top
+44
View File
@@ -303,3 +303,47 @@ function setup() {
# check that the cgroups v2 path is the same for both processes # check that the cgroups v2 path is the same for both processes
[[ "$run_cgroup" == "$exec_cgroup" ]] [[ "$run_cgroup" == "$exec_cgroup" ]]
} }
@test "runc exec should refuse a paused container" {
if [[ "$ROOTLESS" -ne 0 ]]; then
requires rootless_cgroup
fi
requires cgroups_freezer
set_cgroups_path
runc run -d --console-socket "$CONSOLE_SOCKET" ct1
[ "$status" -eq 0 ]
runc pause ct1
[ "$status" -eq 0 ]
# Exec should not timeout or succeed.
runc exec ct1 echo ok
[ "$status" -eq 255 ]
[[ "$output" == *"cannot exec in a paused container"* ]]
}
@test "runc exec --ignore-paused" {
if [[ "$ROOTLESS" -ne 0 ]]; then
requires rootless_cgroup
fi
requires cgroups_freezer
set_cgroups_path
runc run -d --console-socket "$CONSOLE_SOCKET" ct1
[ "$status" -eq 0 ]
runc pause ct1
[ "$status" -eq 0 ]
# Resume the container a bit later.
(
sleep 2
runc resume ct1
) &
# Exec should not timeout or succeed.
runc exec --ignore-paused ct1 echo ok
[ "$status" -eq 0 ]
[ "$output" = "ok" ]
}