diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index c89adad66a..3f06f564b3 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -63,10 +63,15 @@ Units with PrivateTmp=yes automatically gain dependencies of type Wants= and After= on all mounts required to access - /tmp/ and /var/tmp/. They will also gain an automatic + /tmp/ and /var/tmp/ and an automatic After= dependency on - systemd-tmpfiles-setup.service8. - + systemd-tmpfiles-setup.service8, + unless DefaultDependencies=no is specified. If + DefaultDependencies=no is specified, and a + RequiresMountsFor=/tmp/, WantsMountsFor=/tmp/, + After=tmp.mount, or RootDirectory=/RootImage= + are not specified, PrivateTmp=yes is converted to + PrivateTmp=disconnected. Units with PrivateTmp=disconnected automatically gain dependencies of type Wants= and After= on the mount required to access diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c index 10a7b55006..25c12ece25 100644 --- a/src/core/exec-invoke.c +++ b/src/core/exec-invoke.c @@ -2248,10 +2248,8 @@ static int build_environment( } assert(c->private_var_tmp >= 0 && c->private_var_tmp < _PRIVATE_TMP_MAX); - if (needs_sandboxing && c->private_tmp != c->private_var_tmp) { - assert(c->private_tmp == PRIVATE_TMP_DISCONNECTED); - assert(c->private_var_tmp == PRIVATE_TMP_NO); - + if (needs_sandboxing && + c->private_var_tmp == PRIVATE_TMP_NO && c->private_tmp != PRIVATE_TMP_NO) { /* When private tmpfs is enabled only on /tmp/, then explicitly set $TMPDIR to suggest the * service to use /tmp/. */ @@ -3907,14 +3905,16 @@ static int apply_mount_namespace( if (needs_sandboxing) { /* The runtime struct only contains the parent of the private /tmp, which is non-accessible * to world users. Inside of it there's a /tmp that is sticky, and that's the one we want to - * use here. This does not apply when we are using /run/systemd/empty as fallback. */ + * use here. This does not apply when we are using /run/systemd/empty as fallback. */ if (context->private_tmp == PRIVATE_TMP_CONNECTED && runtime->shared) { if (streq_ptr(runtime->shared->tmp_dir, RUN_SYSTEMD_EMPTY)) tmp_dir = runtime->shared->tmp_dir; else if (runtime->shared->tmp_dir) tmp_dir = strjoina(runtime->shared->tmp_dir, "/tmp"); + } + if (context->private_var_tmp == PRIVATE_TMP_CONNECTED && runtime->shared) { if (streq_ptr(runtime->shared->var_tmp_dir, RUN_SYSTEMD_EMPTY)) var_tmp_dir = runtime->shared->var_tmp_dir; else if (runtime->shared->var_tmp_dir) @@ -4624,7 +4624,7 @@ static bool exec_needs_cap_sys_admin(const ExecContext *context, const ExecParam return false; return context->private_users != PRIVATE_USERS_NO || - context->private_tmp != PRIVATE_TMP_NO || + context->private_tmp != PRIVATE_TMP_NO || /* no need to check for private_var_tmp here, private_tmp is never demoted to "no" */ context->private_devices || context->private_network || context->user_namespace_path || diff --git a/src/core/execute.h b/src/core/execute.h index 094b0a3a44..3d5e92cccf 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -333,9 +333,9 @@ typedef struct ExecContext { int bind_log_sockets; int memory_ksm; MemoryTHP memory_thp; - PrivateTmp private_tmp; - PrivateTmp private_var_tmp; /* This is not an independent parameter, but calculated from other - * parameters in unit_patch_contexts(). */ + PrivateTmp private_tmp; /* Those are not independent parameters, but are calculated from */ + PrivateTmp private_var_tmp; /* other parameters in unit_patch_contexts(). */ + bool private_network; bool private_devices; PrivateUsers private_users; diff --git a/src/core/namespace.c b/src/core/namespace.c index f899fa241c..54504564ab 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -756,8 +756,8 @@ static int append_private_tmp(MountList *ml, const NamespaceParameters *p) { assert(ml); assert(p); - assert(p->private_tmp == p->private_var_tmp || - (p->private_tmp == PRIVATE_TMP_DISCONNECTED && p->private_var_tmp == PRIVATE_TMP_NO)); + assert(p->private_tmp >= 0 && p->private_tmp < _PRIVATE_TMP_MAX); + assert(p->private_var_tmp >= 0 && p->private_var_tmp < _PRIVATE_TMP_MAX); if (p->tmp_dir) { assert(p->private_tmp == PRIVATE_TMP_CONNECTED); diff --git a/src/core/unit.c b/src/core/unit.c index bf4542c080..dc158fb335 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -1310,20 +1310,15 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { assert(c->private_var_tmp >= 0 && c->private_var_tmp < _PRIVATE_TMP_MAX); if (c->private_tmp == PRIVATE_TMP_CONNECTED) { - assert(c->private_var_tmp == PRIVATE_TMP_CONNECTED); - r = unit_add_mounts_for(u, "/tmp/", UNIT_DEPENDENCY_FILE, UNIT_MOUNT_WANTS); if (r < 0) return r; + } + if (c->private_var_tmp == PRIVATE_TMP_CONNECTED) { r = unit_add_mounts_for(u, "/var/tmp/", UNIT_DEPENDENCY_FILE, UNIT_MOUNT_WANTS); if (r < 0) return r; - - r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_TMPFILES_SETUP_SERVICE, true, UNIT_DEPENDENCY_FILE); - if (r < 0) - return r; - } else if (c->private_var_tmp == PRIVATE_TMP_DISCONNECTED && !exec_context_with_rootfs(c)) { /* Even if PrivateTmp=disconnected, we still require /var/tmp/ mountpoint to be present, * i.e. /var/ needs to be mounted. See comments in unit_patch_contexts(). */ @@ -1332,6 +1327,12 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { return r; } + if (c->private_tmp == PRIVATE_TMP_CONNECTED || c->private_var_tmp == PRIVATE_TMP_CONNECTED) { + r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_TMPFILES_SETUP_SERVICE, true, UNIT_DEPENDENCY_FILE); + if (r < 0) + return r; + } + if (c->root_image || c->root_mstack) { /* We need to wait for /dev/loopX to appear when doing RootImage=, hence let's add an * implicit dependency on udev. (And for RootMStack= we might need it) */ @@ -4349,8 +4350,8 @@ static PrivateTmp unit_get_private_var_tmp(const Unit *u, const ExecContext *c) assert(c->private_tmp >= 0 && c->private_tmp < _PRIVATE_TMP_MAX); /* Disable disconnected private tmpfs on /var/tmp/ when DefaultDependencies=no and - * RootImage=/RootDirectory= are not set, as /var/ may be a separated partition. - * See issue #37258. */ + * RootImage=/RootDirectory= are not set, as /var/ may be a separate partition. + * See https://github.com/systemd/systemd/issues/37258. */ /* PrivateTmp=yes/no also enables/disables private tmpfs on /var/tmp/. */ if (c->private_tmp != PRIVATE_TMP_DISCONNECTED) @@ -4388,6 +4389,42 @@ static PrivateTmp unit_get_private_var_tmp(const Unit *u, const ExecContext *c) return PRIVATE_TMP_NO; } +static PrivateTmp unit_get_private_tmp(const Unit *u, const ExecContext *c) { + assert(u); + assert(c); + assert(c->private_tmp >= 0 && c->private_tmp < _PRIVATE_TMP_MAX); + + /* Upgrade "PrivateTmp=yes" (a.k.a. 'connected') to 'disconnected' when + * DefaultDependencies=no and RootImage=/RootDirectory= are not set, as /tmp/ may be a + * separate partition. See https://github.com/systemd/systemd/issues/28515. + * + * Note that the change goes in the opposite direction than unit_get_private_var_tmp() + * above. For /var/tmp/, we need to disable the setting, because we don't want to create + * the /var/tmp/ directory if /var/ is a mount point. We don't have this problem with + * /tmp/ because there is no nesting. */ + + if (c->private_tmp != PRIVATE_TMP_CONNECTED || + u->default_dependencies || + exec_context_with_rootfs(c)) + return c->private_tmp; + + /* Even if DefaultDependencies=no, honour tmpfs setting when + * RequiresMountsFor=/WantsMountsFor=/tmp/ is explicitly set. */ + for (UnitMountDependencyType t = 0; t < _UNIT_MOUNT_DEPENDENCY_TYPE_MAX; t++) + if (hashmap_contains(u->mounts_for[t], "/tmp/")) + return c->private_tmp; + + /* Check the same but for After=. */ + Unit *m = manager_get_unit(u->manager, "tmp.mount"); + if (!m) + return c->private_tmp; + + if (unit_has_dependency(u, UNIT_ATOM_AFTER, m)) + return c->private_tmp; + + return PRIVATE_TMP_DISCONNECTED; +} + int unit_patch_contexts(Unit *u) { CGroupContext *cc; ExecContext *ec; @@ -4464,7 +4501,14 @@ int unit_patch_contexts(Unit *u) { ec->restrict_suid_sgid = true; } + /* Table of possible combinations: + * /var/tmp /tmp + * PrivateTmp=no no no + * PrivateTmp=connected connected connected,disconnected + * PrivateTmp=disconnected disconnected,no disconnected + */ ec->private_var_tmp = unit_get_private_var_tmp(u, ec); + ec->private_tmp = unit_get_private_tmp(u, ec); FOREACH_ARRAY(d, ec->directories, _EXEC_DIRECTORY_TYPE_MAX) exec_directory_sort(d);