1
0
mirror of https://github.com/systemd/systemd synced 2025-10-03 18:54:45 +02:00

Compare commits

...

9 Commits

Author SHA1 Message Date
Frantisek Sumsal
4b252eaa31 test: disable nss-systemd for TEST-22 under ASan
When running TEST-22 under ASan, there's a chain of events which causes
`stat` to output an extraneous ASan error message, causing following
fail:

```
+ test -d /tmp/d/1
++ stat -c %U:%G:%a /tmp/d/1
==82==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.
+ test = daemon:daemon:755
.//usr/lib/systemd/tests/testdata/units/testsuite-22.02.sh: line 24: test: =: unary operator expected
```

This is caused by `stat` calling nss which in Arch's configuration calls
the nss-systemd module, that pulls in libasan which causes the $LD_PRELOAD
error message, since `stat` is an uninstrumented binary.

The $LD_PRELOAD variable is explicitly unset for all testsuite-* services
since it causes various issues when calling uninstrumented libraries, so
setting it globally is not an option. Another option would be to set
$LD_PRELOAD for each `stat` call, but that would unnecessarily clutter
the test code.
2021-03-10 11:59:43 +01:00
Zbigniew Jędrzejewski-Szmek
749c4c8ed1
Merge pull request #18553 from Werkov/cgroup-user-instance-controllers
Make (user) instance aware of delegated cgroup controllers
2021-03-10 09:41:40 +01:00
Zbigniew Jędrzejewski-Szmek
ffea01856f
Merge pull request #18942 from keszybz/sd-netlink-prettification
sd-nelink prettification
2021-03-10 09:18:06 +01:00
Zbigniew Jędrzejewski-Szmek
c7209bcfe1 sd-netlink: shorten things a bit 2021-03-09 17:26:24 +01:00
Zbigniew Jędrzejewski-Szmek
4fa024683c sd-netlink: use setsockopt_int() also for NETLINK_ADD/DROP_MEMBERSHIP
We use 'unsigned' as the type, but netlink(7) says the type is 'int'.
It doesn't really matter, since they are both the same size. Let's use
our helper to shorten the code a bit.
2021-03-09 17:26:24 +01:00
Zbigniew Jędrzejewski-Szmek
64a65bab59 sd-netlink: use structured initialization
The casts look somewhat ugly and type-unsafe, but they are equivalent
to what was there before (we initialized a variable from a void*).
2021-03-09 17:26:24 +01:00
Michal Koutný
873446f2e4 Revert "Silence cgroups v1 read-only filesystem warning"
PID 1 will now check upfront which v1 controller hiearchies are
available and modifiable and therefore it will not attempt to touch
them. If we get an EROFS failure then, it points to another
inconsistency so we will report it again. The revert also simplifies the
code a bit.
2021-02-11 16:59:50 +01:00
Michal Koutný
0fa7b50053 core: Make (user) instance aware of delegated cgroup controllers
systemd user instance assumed same controllers are available to it as to
PID 1. That is not true generally, in v1 (legacy, hybrid) we don't delegate any
controllers to anyone and in v2 (unified) we may delegate only subset of
controllers.
The user instance would fail silently when the controller cgroup cannot
be created or the controller cannot be enabled on the unified hierarchy.

The changes in 7b63961415 ("cgroup: Swap cgroup v1 deletion and
migration") caused some attempts of operating on non-delegated
controllers to be logged.

Make the user instance first check what controllers are availble to it
and narrow operations only to these controllers. The original checks are
kept in place.

Note that daemon-reexec needs to be invoked in order to update the set
of unabled controllers after a change.

Fixes: #18047
Fixes: #17862
2021-02-11 16:58:34 +01:00
Michal Koutný
81504017f4 cgroup: Simplify cg_get_path_and_check
The function controller_is_accessible() doesn't do really much in case
of the unified hierarchy. Move common parts into cg_get_path_and_check
and make controller check v1 specific. This is refactoring only.
2021-02-11 11:51:59 +01:00
8 changed files with 95 additions and 128 deletions

View File

@ -527,41 +527,22 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
return 0;
}
static int controller_is_accessible(const char *controller) {
int r;
static int controller_is_v1_accessible(const char *root, const char *controller) {
const char *cpath, *dn;
assert(controller);
/* Checks whether a specific controller is accessible,
* i.e. its hierarchy mounted. In the unified hierarchy all
* controllers are considered accessible, except for the named
* hierarchies */
if (!cg_controller_is_valid(controller))
return -EINVAL;
r = cg_all_unified();
if (r < 0)
return r;
if (r > 0) {
/* We don't support named hierarchies if we are using
* the unified hierarchy. */
if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
return 0;
if (startswith(controller, "name="))
return -EOPNOTSUPP;
} else {
const char *cc, *dn;
dn = controller_to_dirname(controller);
cc = strjoina("/sys/fs/cgroup/", dn);
cpath = strjoina("/sys/fs/cgroup/", dn);
if (root)
/* Also check that:
* - possible subcgroup is created at root,
* - we can modify the hierarchy.
* "Leak" cpath on stack */
cpath = strjoina(cpath, root, "/cgroup.procs");
if (laccess(cc, F_OK) < 0)
if (laccess(cpath, root ? W_OK : F_OK) < 0)
return -errno;
}
return 0;
}
@ -572,10 +553,23 @@ int cg_get_path_and_check(const char *controller, const char *path, const char *
assert(controller);
assert(fs);
/* Check if the specified controller is actually accessible */
r = controller_is_accessible(controller);
if (!cg_controller_is_valid(controller))
return -EINVAL;
r = cg_all_unified();
if (r < 0)
return r;
if (r > 0) {
/* In the unified hierarchy all controllers are considered accessible,
* except for the named hierarchies */
if (startswith(controller, "name="))
return -EOPNOTSUPP;
} else {
/* Check if the specified controller is actually accessible */
r = controller_is_v1_accessible(NULL, controller);
if (r < 0)
return r;
}
return cg_get_path(controller, path, suffix, fs);
}
@ -1892,7 +1886,7 @@ int cg_mask_from_string(const char *value, CGroupMask *ret) {
return 0;
}
int cg_mask_supported(CGroupMask *ret) {
int cg_mask_supported_subtree(const char *root, CGroupMask *ret) {
CGroupMask mask;
int r;
@ -1904,15 +1898,11 @@ int cg_mask_supported(CGroupMask *ret) {
if (r < 0)
return r;
if (r > 0) {
_cleanup_free_ char *root = NULL, *controllers = NULL, *path = NULL;
_cleanup_free_ char *controllers = NULL, *path = NULL;
/* In the unified hierarchy we can read the supported and accessible controllers from
* the top-level cgroup attribute */
r = cg_get_root_path(&root);
if (r < 0)
return r;
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, root, "cgroup.controllers", &path);
if (r < 0)
return r;
@ -1931,7 +1921,7 @@ int cg_mask_supported(CGroupMask *ret) {
} else {
CGroupController c;
/* In the legacy hierarchy, we check which hierarchies are mounted. */
/* In the legacy hierarchy, we check which hierarchies are accessible. */
mask = 0;
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
@ -1942,7 +1932,7 @@ int cg_mask_supported(CGroupMask *ret) {
continue;
n = cgroup_controller_to_string(c);
if (controller_is_accessible(n) >= 0)
if (controller_is_v1_accessible(root, n) >= 0)
mask |= bit;
}
}
@ -1951,6 +1941,17 @@ int cg_mask_supported(CGroupMask *ret) {
return 0;
}
int cg_mask_supported(CGroupMask *ret) {
_cleanup_free_ char *root = NULL;
int r;
r = cg_get_root_path(&root);
if (r < 0)
return r;
return cg_mask_supported_subtree(root, ret);
}
int cg_kernel_controllers(Set **ret) {
_cleanup_set_free_free_ Set *controllers = NULL;
_cleanup_fclose_ FILE *f = NULL;

View File

@ -260,6 +260,7 @@ int cg_slice_to_path(const char *unit, char **ret);
typedef const char* (*cg_migrate_callback_t)(CGroupMask mask, void *userdata);
int cg_mask_supported(CGroupMask *ret);
int cg_mask_supported_subtree(const char *root, CGroupMask *ret);
int cg_mask_from_string(const char *s, CGroupMask *ret);
int cg_mask_to_string(CGroupMask mask, char **ret);

View File

@ -1880,10 +1880,6 @@ int unit_pick_cgroup_path(Unit *u) {
return 0;
}
static int cg_v1_errno_to_log_level(int r) {
return r == -EROFS ? LOG_DEBUG : LOG_WARNING;
}
static int unit_update_cgroup(
Unit *u,
CGroupMask target_mask,
@ -1941,30 +1937,16 @@ static int unit_update_cgroup(
* We perform migration also with whole slices for cases when users don't care about leave
* granularity. Since delegated_mask is subset of target mask, we won't trim slice subtree containing
* delegated units.
*
* If we're in an nspawn container and using legacy cgroups, the controller hierarchies are mounted
* read-only into the container. We skip migration/trim in this scenario since it would fail
* regardless with noisy "Read-only filesystem" warnings.
*/
if (cg_all_unified() == 0) {
r = cg_migrate_v1_controllers(u->manager->cgroup_supported, migrate_mask, u->cgroup_path, migrate_callback, u);
if (r < 0)
log_unit_full_errno(
u,
cg_v1_errno_to_log_level(r),
r,
"Failed to migrate controller cgroups from %s, ignoring: %m",
u->cgroup_path);
log_unit_warning_errno(u, r, "Failed to migrate controller cgroups from %s, ignoring: %m", u->cgroup_path);
is_root_slice = unit_has_name(u, SPECIAL_ROOT_SLICE);
r = cg_trim_v1_controllers(u->manager->cgroup_supported, ~target_mask, u->cgroup_path, !is_root_slice);
if (r < 0)
log_unit_full_errno(
u,
cg_v1_errno_to_log_level(r),
r,
"Failed to delete controller cgroups %s, ignoring: %m",
u->cgroup_path);
log_unit_warning_errno(u, r, "Failed to delete controller cgroups %s, ignoring: %m", u->cgroup_path);
}
/* Set attributes */
@ -3151,7 +3133,7 @@ int manager_setup_cgroup(Manager *m) {
(void) cg_set_attribute("memory", "/", "memory.use_hierarchy", "1");
/* 8. Figure out which controllers are supported */
r = cg_mask_supported(&m->cgroup_supported);
r = cg_mask_supported_subtree(m->cgroup_root, &m->cgroup_supported);
if (r < 0)
return log_error_errno(r, "Failed to determine supported controllers: %m");

View File

@ -32,7 +32,6 @@ static int genl_message_new(sd_netlink *nl, sd_genl_family_t family, uint16_t nl
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
const NLType *genl_cmd_type, *nl_type;
const NLTypeSystem *type_system;
struct genlmsghdr *genl;
size_t size;
int r;
@ -63,9 +62,11 @@ static int genl_message_new(sd_netlink *nl, sd_genl_family_t family, uint16_t nl
m->hdr->nlmsg_type = nlmsg_type;
type_get_type_system(nl_type, &m->containers[0].type_system);
genl = NLMSG_DATA(m->hdr);
genl->cmd = cmd;
genl->version = genl_families[family].version;
*(struct genlmsghdr *) NLMSG_DATA(m->hdr) = (struct genlmsghdr) {
.cmd = cmd,
.version = genl_families[family].version,
};
*ret = TAKE_PTR(m);

View File

@ -144,7 +144,6 @@ static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *da
size_t message_length;
struct nlmsghdr *new_hdr;
struct rtattr *rta;
unsigned i;
int offset;
assert(m);
@ -172,7 +171,7 @@ static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *da
rtattr_append_attribute_internal(rta, type, data, data_length);
/* if we are inside containers, extend them */
for (i = 0; i < m->n_containers; i++)
for (unsigned i = 0; i < m->n_containers; i++)
GET_CONTAINER(m, i)->rta_len += RTA_SPACE(data_length);
/* update message size */
@ -643,7 +642,6 @@ int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type) {
}
int sd_netlink_message_cancel_array(sd_netlink_message *m) {
unsigned i;
uint32_t rta_len;
assert_return(m, -EINVAL);
@ -652,7 +650,7 @@ int sd_netlink_message_cancel_array(sd_netlink_message *m) {
rta_len = GET_CONTAINER(m, (m->n_containers - 1))->rta_len;
for (i = 0; i < m->n_containers; i++)
for (unsigned i = 0; i < m->n_containers; i++)
GET_CONTAINER(m, i)->rta_len -= rta_len;
m->hdr->nlmsg_len -= rta_len;
@ -1283,7 +1281,6 @@ int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *genl) {
const NLType *nl_type;
uint16_t type;
size_t size;
unsigned i;
int r;
assert_return(m, -EINVAL);
@ -1293,7 +1290,7 @@ int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *genl) {
if (!m->sealed)
rtnl_message_seal(m);
for (i = 1; i <= m->n_containers; i++)
for (unsigned i = 1; i <= m->n_containers; i++)
m->containers[i].attributes = mfree(m->containers[i].attributes);
m->n_containers = 0;

View File

@ -29,7 +29,6 @@ int socket_open(int family) {
static int broadcast_groups_get(sd_netlink *nl) {
_cleanup_free_ uint32_t *groups = NULL;
socklen_t len = 0, old_len;
unsigned i, j;
int r;
assert(nl);
@ -37,11 +36,11 @@ static int broadcast_groups_get(sd_netlink *nl) {
r = getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &len);
if (r < 0) {
if (errno == ENOPROTOOPT) {
if (errno != ENOPROTOOPT)
return -errno;
nl->broadcast_group_dont_leave = true;
return 0;
} else
return -errno;
}
if (len == 0)
@ -64,23 +63,15 @@ static int broadcast_groups_get(sd_netlink *nl) {
if (r < 0)
return r;
for (i = 0; i < len; i++) {
for (j = 0; j < sizeof(uint32_t) * 8; j++) {
uint32_t offset;
unsigned group;
offset = 1U << j;
if (!(groups[i] & offset))
continue;
group = i * sizeof(uint32_t) * 8 + j + 1;
for (unsigned i = 0; i < len; i++)
for (unsigned j = 0; j < sizeof(uint32_t) * 8; j++)
if (groups[i] & (1U << j)) {
unsigned group = i * sizeof(uint32_t) * 8 + j + 1;
r = hashmap_put(nl->broadcast_group_refs, UINT_TO_PTR(group), UINT_TO_PTR(1));
if (r < 0)
return r;
}
}
return 0;
}
@ -104,11 +95,7 @@ int socket_bind(sd_netlink *nl) {
if (r < 0)
return -errno;
r = broadcast_groups_get(nl);
if (r < 0)
return r;
return 0;
return broadcast_groups_get(nl);
}
static unsigned broadcast_group_get_ref(sd_netlink *nl, unsigned group) {
@ -130,17 +117,12 @@ static int broadcast_group_set_ref(sd_netlink *nl, unsigned group, unsigned n_re
}
static int broadcast_group_join(sd_netlink *nl, unsigned group) {
int r;
assert(nl);
assert(nl->fd >= 0);
assert(group > 0);
r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group));
if (r < 0)
return -errno;
return 0;
/* group is "unsigned", but netlink(7) says the argument for NETLINK_ADD_MEMBERSHIP is "int" */
return setsockopt_int(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, group);
}
int socket_broadcast_group_ref(sd_netlink *nl, unsigned group) {
@ -173,8 +155,6 @@ int socket_broadcast_group_ref(sd_netlink *nl, unsigned group) {
}
static int broadcast_group_leave(sd_netlink *nl, unsigned group) {
int r;
assert(nl);
assert(nl->fd >= 0);
assert(group > 0);
@ -182,11 +162,8 @@ static int broadcast_group_leave(sd_netlink *nl, unsigned group) {
if (nl->broadcast_group_dont_leave)
return 0;
r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, &group, sizeof(group));
if (r < 0)
return -errno;
return 0;
/* group is "unsigned", but netlink(7) says the argument for NETLINK_DROP_MEMBERSHIP is "int" */
return setsockopt_int(nl->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, group);
}
int socket_broadcast_group_unref(sd_netlink *nl, unsigned group) {
@ -240,8 +217,6 @@ int socket_write_message(sd_netlink *nl, sd_netlink_message *m) {
int socket_writev_message(sd_netlink *nl, sd_netlink_message **m, size_t msgcount) {
_cleanup_free_ struct iovec *iovs = NULL;
ssize_t k;
size_t i;
assert(nl);
assert(m);
@ -251,13 +226,13 @@ int socket_writev_message(sd_netlink *nl, sd_netlink_message **m, size_t msgcoun
if (!iovs)
return -ENOMEM;
for (i = 0; i < msgcount; i++) {
for (size_t i = 0; i < msgcount; i++) {
assert(m[i]->hdr != NULL);
assert(m[i]->hdr->nlmsg_len > 0);
iovs[i] = IOVEC_MAKE(m[i]->hdr, m[i]->hdr->nlmsg_len);
}
k = writev(nl->fd, iovs, msgcount);
ssize_t k = writev(nl->fd, iovs, msgcount);
if (k < 0)
return -errno;
@ -325,7 +300,6 @@ int socket_read_message(sd_netlink *rtnl) {
struct iovec iov = {};
uint32_t group = 0;
bool multi_part = false, done = false;
struct nlmsghdr *new_msg;
size_t len;
int r;
unsigned i = 0;
@ -372,7 +346,7 @@ int socket_read_message(sd_netlink *rtnl) {
}
}
for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) {
for (struct nlmsghdr *new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
const NLType *nl_type;

View File

@ -19,7 +19,6 @@
static int nft_message_new(sd_netlink *nfnl, sd_netlink_message **ret, int family, uint16_t type, uint16_t flags) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
struct nfgenmsg *nfh;
const NLType *nl_type;
size_t size;
int r;
@ -57,10 +56,11 @@ static int nft_message_new(sd_netlink *nfnl, sd_netlink_message **ret, int famil
m->hdr->nlmsg_len = size;
m->hdr->nlmsg_type = NFNL_SUBSYS_NFTABLES << 8 | type;
nfh = NLMSG_DATA(m->hdr);
nfh->nfgen_family = family;
nfh->version = NFNETLINK_V0;
nfh->res_id = nfnl->serial;
*(struct nfgenmsg*) NLMSG_DATA(m->hdr) = (struct nfgenmsg) {
.nfgen_family = family,
.version = NFNETLINK_V0,
.res_id = nfnl->serial,
};
*ret = TAKE_PTR(m);
return 0;
@ -68,17 +68,17 @@ static int nft_message_new(sd_netlink *nfnl, sd_netlink_message **ret, int famil
static int sd_nfnl_message_batch(sd_netlink *nfnl, sd_netlink_message **ret, int v) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
struct nfgenmsg *nfh;
int r;
r = message_new(nfnl, &m, v);
if (r < 0)
return r;
nfh = NLMSG_DATA(m->hdr);
nfh->nfgen_family = AF_UNSPEC;
nfh->version = NFNETLINK_V0;
nfh->res_id = NFNL_SUBSYS_NFTABLES;
*(struct nfgenmsg*) NLMSG_DATA(m->hdr) = (struct nfgenmsg) {
.nfgen_family = AF_UNSPEC,
.version = NFNETLINK_V0,
.res_id = NFNL_SUBSYS_NFTABLES,
};
*ret = TAKE_PTR(m);
return r;

View File

@ -4,4 +4,15 @@ TEST_DESCRIPTION="Tmpfiles related tests"
TEST_NO_QEMU=1
. $TEST_BASE_DIR/test-functions
test_append_files() {
if [[ "$IS_BUILT_WITH_ASAN" == "yes" ]]; then
if [[ -z "$initdir" ]]; then
echo >&2 "\$initdir is not defined, can't continue"
exit 1
fi
sed -i "s/systemd//g" "$initdir/etc/nsswitch.conf"
fi
}
do_test "$@" 22