Commit Graph

2013 Commits

Author SHA1 Message Date
Aleksa Sarai d0f2c25f52 cgroup2: devices: replace all existing filters when attaching
In the normal cases (only one existing filter or no existing filters),
just make use of BPF_F_REPLACE if there is one existing filter. However
if there is more than one filter applied, we should probably remove all
other filters since the alternative is that we will never remove our old
filters.

The only two other viable ways of solving this problem would be to use
BPF pins to either pin the eBPF program using a predictable name (so we
can always only replace *our* programs) or to switch away from custom
programs and instead use eBPF maps (which are pinned) and thus we just
update the map conntents to update the ruleset. Unfortunately these both
would add a hard requirement of bpffs and would require at least a minor
rewrite of the eBPF filtering code -- which is better left for another
time.

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
2021-05-23 17:55:19 +10:00
Aleksa Sarai 98a3c0e4db cgroup2: devices: switch to emulator for cgroupv1 parity
There were several issues with the previous cgroupv2 devices filter
generator implementation, stemming from the previous implementation
using a few too many tricks to implement the correct cgroup behaviour
(rules were handled in reverse order, with wildcards having particularly
special interpretations). As a result, some slightly odd configurations
with rules in specific orders could result in incorrect filters being
generated.

By switching to the emulator which is already used by cgroupv1, we can
guarantee that the behaviour of filters in both cgroup versions will be
identical, as well as making use of the hardenings in the emulator (not
allowing users to add deny rules the kernel will ignore).

(Note that because the ordering of the devices emulator rules is
deterministic and based on the rule value, the existing test rules had
to be reordered slightly.)

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
2021-05-23 17:55:15 +10:00
Aleksa Sarai dcc1cf7c1c devices: add emulator.Rules shorthand
The devices cgroup emulator is also useful for removing unneeded rules
as well as computing what the final default-allow state of the filter
will be (allow-list or deny-list).

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
2021-05-23 17:55:12 +10:00
Aleksa Sarai 54904516e6 libcontainer: fix integration failure in "make test"
When running inside a Docker container, systemd is not available. The
new TestFdLeaksSystemd forgot to include the relevant t.Skip section.

Fixes: a7feb42395 ("libct/int: add TestFdLeaksSystemd")
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
2021-05-23 17:55:09 +10:00
Aleksa Sarai c7c70ce810 *: clean t.Skip messages
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
2021-05-23 17:53:01 +10:00
Peter Hunt a95237f816 libctr/cg/systemd: export rangeToBits
It's both a useful function, and sufficiently complex to discourage copying

Signed-off-by: Peter Hunt <pehunt@redhat.com>
2021-05-21 10:18:34 -04:00
Akihiro Suda 4d87573871 Merge pull request #2940 from kolyshkin/intelrdt-unit-test
libct/intelrdt: fix unit test
2021-05-20 13:43:20 +09:00
Kir Kolyshkin b93666ebc1 libct/cg/fs2: setFreezer: wait until frozen
According to cgroup v2 documentation [1]:

> Freezing of the cgroup may take some time; when this action is
> completed, the “frozen” value in the cgroup.events control file will
> be updated to “1” and the corresponding notification will be issued.

Implement polling of cgroup.events, waiting for "frozen 1" to appear.
In case something goes wrong, limit the maximum number of retries and
return "undefined" after some time (currently 10s).

[1] https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-05-19 12:15:24 -07:00
Kir Kolyshkin 1069e4e9da libct/cg/fs2: optimize setFreezer more
Before this patch, setFreezer does

- open/read/close (to check if the freezer is supported)
- open/write/close (to set the value)
- open/read/close (to check the value)

Three opens is a bit excessive. Refactor to only open the file once:

- open (to check if the freezer is supported)
- write (to set the value)
- seek/read (to check the value)
- close

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-05-19 12:10:56 -07:00
Kir Kolyshkin 5d193188db libct/cg/fs2: optimize setFreezer
In case configs.Undefined or any wrong value is passed, there is no need
to check whether the freezer is supported.

Move arguments check to the beginning to avoid an unnecessary call to
supportFreezer().

While at it, simplify the "whether to return an error if freezer is not
supported" check.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-05-19 12:10:56 -07:00
Aleksa Sarai 0ca91f44f1 rootfs: add mount destination validation
Because the target of a mount is inside a container (which may be a
volume that is shared with another container), there exists a race
condition where the target of the mount may change to a path containing
a symlink after we have sanitised the path -- resulting in us
inadvertently mounting the path outside of the container.

This is not immediately useful because we are in a mount namespace with
MS_SLAVE mount propagation applied to "/", so we cannot mount on top of
host paths in the host namespace. However, if any subsequent mountpoints
in the configuration use a subdirectory of that host path as a source,
those subsequent mounts will use an attacker-controlled source path
(resolved within the host rootfs) -- allowing the bind-mounting of "/"
into the container.

While arguably configuration issues like this are not entirely within
runc's threat model, within the context of Kubernetes (and possibly
other container managers that provide semi-arbitrary container creation
privileges to untrusted users) this is a legitimate issue. Since we
cannot block mounting from the host into the container, we need to block
the first stage of this attack (mounting onto a path outside the
container).

The long-term plan to solve this would be to migrate to libpathrs, but
as a stop-gap we implement libpathrs-like path verification through
readlink(/proc/self/fd/$n) and then do mount operations through the
procfd once it's been verified to be inside the container. The target
could move after we've checked it, but if it is inside the container
then we can assume that it is safe for the same reason that libpathrs
operations would be safe.

A slight wrinkle is the "copyup" functionality we provide for tmpfs,
which is the only case where we want to do a mount on the host
filesystem. To facilitate this, I split out the copy-up functionality
entirely so that the logic isn't interspersed with the regular tmpfs
logic. In addition, all dependencies on m.Destination being overwritten
have been removed since that pattern was just begging to be a source of
more mount-target bugs (we do still have to modify m.Destination for
tmpfs-copyup but we only do it temporarily).

Fixes: CVE-2021-30465
Reported-by: Etienne Champetier <champetier.etienne@gmail.com>
Co-authored-by: Noah Meyerhans <nmeyerha@amazon.com>
Reviewed-by: Samuel Karp <skarp@amazon.com>
Reviewed-by: Kir Kolyshkin <kolyshkin@gmail.com> (@kolyshkin)
Reviewed-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
2021-05-19 16:58:35 +10:00
Aleksa Sarai c01a56034f merge branch 'pr-2954'
Giuseppe Scrivano (1):
  libcontainer: honor seccomp defaultErrnoRet

LGTMs: kolyshkin cyphar
Closes #2954
2021-05-18 17:00:37 +10:00
Giuseppe Scrivano c61f606254 libcontainer: honor seccomp defaultErrnoRet
https://github.com/opencontainers/runtime-spec/pull/1087 added support
for defaultErrnoRet to the OCI runtime specs.

If a defaultErrnoRet is specified, disable patching the generated
libseccomp cBPF.

Closes: https://github.com/opencontainers/runc/issues/2943

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2021-05-17 09:23:32 +02:00
Mrunal Patel 5c4ccc2f72 Merge pull request #2938 from kolyshkin/libct-int-nts
libcontainer/integration: nits
2021-05-14 19:15:38 -04:00
Kir Kolyshkin 4f8ccc5ff5 libct/cg/sd/v2: call initPath from Path
Sometimes Path() is called before m.path is initialized (in particular,
this happens from (*linuxContainer).newInitConfig), so we do need to
make sure to call initPath.

This fixes the following integration tests (for cgroup v2 + systemd case,
currently not enabled -- to be enabled by further commits):

 *  runc run (blkio weight)
 * runc run (cgroupv2 mount inside container)

Fixes: ff692f289b ("Fix cgroup2 mount for rootless case")
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-05-07 22:05:56 -07:00
Kir Kolyshkin a7feb42395 libct/int: add TestFdLeaksSystemd
Add a test to check that container.Run do not leak file descriptors.

Before the previous commit, it fails like this:

    exec_test.go:2030: extra fd 8 -> socket:[659703]
    exec_test.go:2030: extra fd 11 -> socket:[658715]
    exec_test.go:2033: found 2 extra fds after container.Run

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-05-06 12:37:55 -07:00
Kir Kolyshkin c7f847ed3a libct/cg/sd: use global dbus connection
Using per cgroup manager dbus connection instances means
that every cgroup manager instance gets a new connection,
and those connections are never closed, ultimately resulting
in file descriptors limit being hit.

Revert back to using a single global dbus connection for everything,
without changing the callers.

NOTE that it is assumed a runtime can't use both root and rootless
dbus at the same time. If this happens, we panic.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-05-06 12:37:55 -07:00
Kir Kolyshkin 99c5c504ea libct/cg/sd: introduce and use getManagerProperty
Commit 47ef9a104f forgot to wrap GetManagerProperty("ControlGroup")
into retryOnDisconnect. Since there's one other user of
GetManagerProperty, add getManagerProperty wrapper and use it.

Fixes: 47ef9a104f

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-05-06 12:37:55 -07:00
Kir Kolyshkin 0fabed76c1 libct/int/checkpoint_test: use kill(0) for pid check
os.FindProcess never returns an error on Unix/Linux.
Use kill(0) to actually check if the process exists.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-05-06 12:36:24 -07:00
Kir Kolyshkin 7eb1405b60 libct/int/checkpoint_test: use waitProcess helper
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-05-06 12:36:24 -07:00
Kir Kolyshkin 72d7a8249d libct/int/checkpoint_test: use t.Helper
Otherwise showFile log statements show its own file/line info,
rather than that of the caller's.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-05-06 12:36:24 -07:00
Kir Kolyshkin bcca796848 libct/int: simplify/fix showing errors
Instead of manually figuring out the file and line number of the caller,
use t.Helper() so t.Fatal prints the correct one.

Before:
> utils_test.go:85: exec_test.go:536: unexpected error: container_linux.go:380: starting container process caused: exec: "catt": executable file not found in $PATH

After:
> exec_test.go:536: unexpected error: container_linux.go:380: starting container process caused: exec: "catt": executable file not found in $PATH

(the error is introduced by s/cat/catt/)

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-05-06 12:36:24 -07:00
Kir Kolyshkin 524abc59f4 freezer: add delay after freeze
I hate to keep adding those kludges, but lately TestFreeze (and
TestSystemdFreeze) from libcontainer/integration fails a lot. The
failure comes and goes, and is probably this is caused by a slow host
allocated for the test, and a slow VM on top of it.

To remediate, add a small sleep on every 25th iteration in between
asking the kernel to freeze and checking its status.

In the worst case scenario (failure to freeze), this adds about 0.4 ms
(40 x 10 us) to the duration of the call.

It is hard to measure how this affects CI as GHA plays a roulette when
allocating a node to run the test on, but it seems to help. With
additional debug info, I saw somewhat frequent "frozen after 24 retries"
or "frozen after 49 retries", meaning it succeeded right after the added
sleep.

While at it, rewrite/improve the comments.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-05-06 10:51:46 -07:00
Kir Kolyshkin e1d842cfa6 libct/intelrdt: fix unit test
1. These tests can't be run in parallel since they do check
   a global variable (mbaScEnabled).

2. findIntelRdtMountpointDir() relies on mbaScEnabled to be initially
   set to the default value (false) and this the test fails if run
   more than once:

> go test -count 2
> ...
> intelrdt_test.go:243: expected mbaScEnabled=false, got true
>    --- FAIL: TestFindIntelRdtMountpointDir/Valid_mountinfo_with_MBA_Software_Controller_disabled (0.00s)

Fixes: 2c70d2384
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-05-05 14:07:10 -07:00
Akihiro Suda f9176c909a Merge pull request #2909 from kolyshkin/libct-int-better-names
libct/int: misc cleanups and fixes
2021-05-06 02:21:19 +09:00
Aleksa Sarai d279ebd97d merge branch 'pr-2925'
Akihiro Suda (4):
  Revert "Makefile: rm go 1.13 workaround"
  libcontainer: avoid using t.Cleanup
  go.mod: demote to Go 1.13
  CI: enable Go 1.13 again

LGTMs: kolyskhin cyphar
Closes #2925
2021-05-02 01:23:39 +10:00
Kir Kolyshkin b15ddb4739 Merge pull request #2919 from cyphar/warn-unknown-architecture
cloned_binary: switch from #error to #warning for SYS_memfd_create
2021-04-30 10:47:58 -07:00
Akihiro Suda c061bc78aa Merge pull request #2906 from kolyshkin/set-allow-nil 2021-04-30 21:01:15 +09:00
Akihiro Suda 45f49e8fca libcontainer: avoid using t.Cleanup
t.Cleanup is not present in Go 1.13.
Dockre/Moby still builds runc with Go 1.13, so we should still support
Go 1.13.

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2021-04-30 19:13:05 +09:00
Aleksa Sarai 016717a550 merge branch 'pr-2926'
Sebastiaan van Stijn (1):
  cgroups/systemd: replace deprecated dbus functions

LGTMs: AkihiroSuda cyphar
Closes #2926
2021-04-30 18:14:00 +10:00
Akihiro Suda 8f85f4f1f2 Merge pull request #2921 from kolyshkin/faster-openfile 2021-04-30 17:06:37 +09:00
Kir Kolyshkin abf12ce0db libc/cg: improve Manager docs
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-29 15:30:12 -07:00
Kir Kolyshkin 3f65946756 libct/cg: make Set accept configs.Resources
A cgroup manager's Set method sets cgroup resources, but historically
it was accepting configs.Cgroups.

Refactor it to accept resources only. This is an improvement from the
API point of view, as the method can not change cgroup configuration
(such as path to the cgroup etc), it can only set (modify) its
resources/limits.

This also lays the foundation for complicated resource updates, as now
Set has two sets of resources -- the one that was previously specified
during cgroup manager creation (or the previous Set), and the one passed
in the argument, so it could deduce the difference between these. This
is a long term goal though.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-29 15:24:19 -07:00
Kir Kolyshkin af0710a0f8 libct/cg/sd/v2: fix Set argument
For some reason, systemd cgroup v2 driver's Set is not using its
container argument when generating systemd unit properties.

This bug is not detected by our update tests as we run a new binary
every time and thus a new instance of a cgroup manager.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-29 15:12:56 -07:00
Kir Kolyshkin 850b2c47b2 libct/cg/fscommon.OpenFile: speed up ro case
Commit 88e8350de2, among the other things, replaced filepath.Join with
securejoin.SecureJoin for both reads and writes to cgroupfs.

Commits e76ac1c054 and 31f0f5b7e0 switched more code to use
fscommon.ReadFile (and thus securejoin). Commit 0228226e6d introduced
fscommon.OpenFile (which uses securejoin as the fallback if openat2(2)
is not available, which is the case for older kernels), and commit
c95e69007c switched most of cgroup/fs[2] code to use it.

As a result, fs.GetStats() method became noticeable slower, mostly due
to securejoin calling os.Lstat and filepath.Clean.

Using securejoin as a security measure for cgroupfs files is
not well justified, as cgroupfs do not contain symlinks, and none of the
code using it have uncleaned paths. In particular, fs/fs2/systemd
managers do check and sanitize their paths.

This commit modifies the code to not use securejoin. Instead, it checks
that the opened file is indeed on cgroupfs.

Using BenchmarkGetStats on a CentOS 8 VM, I see the following
improvement:

Before:
> BenchmarkGetStats-8               8376            625135 ns/op

After:
> BenchmarkGetStats-8   	   12226	    485015 ns/op

An intermediate version, with no fstatfs to check fstype:
> BenchmarkGetStats-8              13162            452281 ns/op

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-29 14:55:02 -07:00
Sebastiaan van Stijn 71a8aee846 cgroups/systemd: replace deprecated dbus functions
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-04-29 22:07:02 +02:00
Akihiro Suda c2c35aefee Merge pull request #2911 from kolyshkin/debug-enable-file-line
logging: enable file/line info if --debug is set
2021-04-28 12:07:21 +09:00
Kir Kolyshkin 47ef9a104f libct/cg/sd: retry on dbus disconnect
Instead of reconnecting to dbus after some failed operations, and
returning an error (so a caller has to retry), reconnect AND retry
in place for all such operations.

This should fix issues caused by a stale dbus connection after e.g.
a dbus daemon restart.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-27 16:19:49 -07:00
Shiming Zhang 6122bc8beb Privatize NewUserSystemDbus
Signed-off-by: Shiming Zhang <wzshiming@foxmail.com>
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-27 16:16:27 -07:00
Shiming Zhang 15fee9899f libct/cg/sd: add renew dbus connection
[@kolyshkin: doc nits, use dbus.ErrClosed and isDbusError]

Signed-off-by: Shiming Zhang <wzshiming@foxmail.com>
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-27 16:16:05 -07:00
Kir Kolyshkin bacfc2c2f9 libct/cg/sd: add isDbusError
Generalize isUnitExists as isDbusError, and use errors.As while at it
(which can handle wrapped errors as well).

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-27 16:14:04 -07:00
Shiming Zhang cdbed6f02f libct/cg/sd: add dbus manager
[@kolyshkin: documentation nits]

Signed-off-by: Shiming Zhang <wzshiming@foxmail.com>
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-27 16:02:39 -07:00
Kir Kolyshkin 9efd8466ab libct/cg/fscommon.OpenFile: reverse checks order
In case openat2() is not available, it does not make sense to calculate
relpath (and check if path has /sys/fs/cgroup prefix).

Reverse the order of checks to not do that in case openat2 is not
available.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-27 09:51:31 -07:00
Kir Kolyshkin 0bee5e0b01 libct/cg/fs: add GetStats benchmark
On my CentOS 8 VM it shows:

> BenchmarkGetStats-8   	    8376	    625135 ns/op

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-27 09:51:20 -07:00
Aleksa Sarai d3cee12a78 cloned_binary: switch from #error to #warning for SYS_memfd_create
We shouldn't refuse to build on architectures just because we don't know
what the syscall number of memfd_create(2) is. In addition, use the
correct defined(...) macros for ppc64 (these are the ones glibc uses).

Fixes: 3aead32ea2 ("nsenter: hard-code memfd_create(2) syscall numbers")
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
2021-04-26 18:08:32 +10:00
Mrunal Patel 42a18e7f02 Merge pull request #2818 from kolyshkin/rootless-cgroup2-mount
Fix cgroup2 mount for rootless case
2021-04-22 17:04:19 -07:00
Akihiro Suda d061b41d9d Merge pull request #2917 from kolyshkin/validate-mounts
Validate mounts
2021-04-22 18:31:25 +09:00
Kir Kolyshkin fcd7fe85e1 libct/cg/fs/freezer: make sure to thaw on failure
Function (*FreezerGroup).Set has a few paths where in can return an
error. In any case, if an error is returned, we failed to freeze,
and we need to thaw to avoid leaving the cgroup in a stuck state.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-20 15:57:59 -07:00
Kir Kolyshkin ff692f289b Fix cgroup2 mount for rootless case
In case of rootless, cgroup2 mount is not possible (see [1] for more
details), so since commit 9c81440fb5 runc bind-mounts the whole
/sys/fs/cgroup into container.

Problem is, if cgroupns is enabled, /sys/fs/cgroup inside the container
is supposed to show the cgroup files for this cgroup, not the root one.

The fix is to pass through and use the cgroup path in case cgroup2
mount failed, cgroupns is enabled, and the path is non-empty.

Surely this requires the /sys/fs/cgroup mount in the spec, so modify
runc spec --rootless to keep it.

Before:

	$ ./runc run aaa
	# find /sys/fs/cgroup/ -type d
	/sys/fs/cgroup
	/sys/fs/cgroup/user.slice
	/sys/fs/cgroup/user.slice/user-1000.slice
	/sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service
	...
	# ls -l /sys/fs/cgroup/cgroup.controllers
	-r--r--r--    1 nobody   nogroup          0 Feb 24 02:22 /sys/fs/cgroup/cgroup.controllers
	# wc -w /sys/fs/cgroup/cgroup.procs
	142 /sys/fs/cgroup/cgroup.procs
	# cat /sys/fs/cgroup/memory.current
	cat: can't open '/sys/fs/cgroup/memory.current': No such file or directory

After:

	# find /sys/fs/cgroup/ -type d
	/sys/fs/cgroup/
	# ls -l /sys/fs/cgroup/cgroup.controllers
	-r--r--r--    1 root     root             0 Feb 24 02:43 /sys/fs/cgroup/cgroup.controllers
	# wc -w /sys/fs/cgroup/cgroup.procs
	2 /sys/fs/cgroup/cgroup.procs
	# cat /sys/fs/cgroup/memory.current
	577536

[1] https://github.com/opencontainers/runc/issues/2158

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-20 12:35:40 -07:00
Kir Kolyshkin 3826db196d libct/rootfs/mountCgroupV2: minor refactor
1. s/cgroupPath/dest/

2. don't hardcode /sys/fs/cgroup

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-20 12:30:30 -07:00