core: Try to prevent infinite recursive template instantiation
To prevent situations like in #17602 from happening, let's drop direct recursive template dependencies. These will almost certainly lead to infinite recursion so let's drop them immediately to avoid instantiating potentially thousands of irrelevant units. Example of a template that would lead to infinite recursion which is caught by this check: notify@.service: ``` [Unit] Wants=notify@%n.service ```
This commit is contained in:
@@ -829,6 +829,71 @@ static void test_config_parse_memory_limit(void) {
|
||||
|
||||
}
|
||||
|
||||
static void test_contains_instance_specifier_superset(void) {
|
||||
assert_se(contains_instance_specifier_superset("foobar@a%i"));
|
||||
assert_se(contains_instance_specifier_superset("foobar@%ia"));
|
||||
assert_se(contains_instance_specifier_superset("foobar@%n"));
|
||||
assert_se(contains_instance_specifier_superset("foobar@%n.service"));
|
||||
assert_se(contains_instance_specifier_superset("foobar@%N"));
|
||||
assert_se(contains_instance_specifier_superset("foobar@%N.service"));
|
||||
assert_se(contains_instance_specifier_superset("foobar@baz.%N.service"));
|
||||
assert_se(contains_instance_specifier_superset("@%N.service"));
|
||||
assert_se(contains_instance_specifier_superset("@%N"));
|
||||
assert_se(contains_instance_specifier_superset("@%a%N"));
|
||||
|
||||
assert_se(!contains_instance_specifier_superset("foobar@%i.service"));
|
||||
assert_se(!contains_instance_specifier_superset("foobar%ia.service"));
|
||||
assert_se(!contains_instance_specifier_superset("foobar@%%n.service"));
|
||||
assert_se(!contains_instance_specifier_superset("foobar@baz.service"));
|
||||
assert_se(!contains_instance_specifier_superset("%N.service"));
|
||||
assert_se(!contains_instance_specifier_superset("%N"));
|
||||
assert_se(!contains_instance_specifier_superset("@%aN"));
|
||||
assert_se(!contains_instance_specifier_superset("@%a%b"));
|
||||
}
|
||||
|
||||
static void test_unit_is_recursive_template_dependency(void) {
|
||||
_cleanup_(manager_freep) Manager *m = NULL;
|
||||
Unit *u;
|
||||
int r;
|
||||
|
||||
r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m);
|
||||
if (manager_errno_skip_test(r)) {
|
||||
log_notice_errno(r, "Skipping test: manager_new: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
assert_se(r >= 0);
|
||||
assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
|
||||
|
||||
assert_se(u = unit_new(m, sizeof(Service)));
|
||||
assert_se(unit_add_name(u, "foobar@1.service") == 0);
|
||||
u->fragment_path = strdup("/foobar@.service");
|
||||
|
||||
assert_se(hashmap_put_strdup(&m->unit_id_map, "foobar@foobar@123.service", "/foobar@.service"));
|
||||
assert_se(hashmap_put_strdup(&m->unit_id_map, "foobar@foobar@456.service", "/custom.service"));
|
||||
|
||||
/* Test that %n, %N and any extension of %i specifiers in the instance are detected as recursive. */
|
||||
assert_se(unit_is_likely_recursive_template_dependency(u, "foobar@foobar@123.service", "foobar@%N.service") == 1);
|
||||
assert_se(unit_is_likely_recursive_template_dependency(u, "foobar@foobar@123.service", "foobar@%n.service") == 1);
|
||||
assert_se(unit_is_likely_recursive_template_dependency(u, "foobar@foobar@123.service", "foobar@a%i.service") == 1);
|
||||
assert_se(unit_is_likely_recursive_template_dependency(u, "foobar@foobar@123.service", "foobar@%ia.service") == 1);
|
||||
assert_se(unit_is_likely_recursive_template_dependency(u, "foobar@foobar@123.service", "foobar@%x%n.service") == 1);
|
||||
/* Test that %i on its own is not detected as recursive. */
|
||||
assert_se(unit_is_likely_recursive_template_dependency(u, "foobar@foobar@123.service", "foobar@%i.service") == 0);
|
||||
/* Test that a specifier other than %i, %n and %N is not detected as recursive. */
|
||||
assert_se(unit_is_likely_recursive_template_dependency(u, "foobar@foobar@123.service", "foobar@%xn.service") == 0);
|
||||
/* Test that an expanded specifier is not detected as recursive. */
|
||||
assert_se(unit_is_likely_recursive_template_dependency(u, "foobar@foobar@123.service", "foobar@foobar@123.service") == 0);
|
||||
/* Test that a dependency with a custom fragment path is not detected as recursive. */
|
||||
assert_se(unit_is_likely_recursive_template_dependency(u, "foobar@foobar@456.service", "foobar@%n.service") == 0);
|
||||
/* Test that a dependency without a fragment path is not detected as recursive. */
|
||||
assert_se(unit_is_likely_recursive_template_dependency(u, "foobar@foobar@789.service", "foobar@%n.service") == 0);
|
||||
/* Test that a dependency with a different prefix is not detected as recursive. */
|
||||
assert_se(unit_is_likely_recursive_template_dependency(u, "quux@foobar@123.service", "quux@%n.service") == 0);
|
||||
/* Test that a dependency of a different type is not detected as recursive. */
|
||||
assert_se(unit_is_likely_recursive_template_dependency(u, "foobar@foobar@123.mount", "foobar@%n.mount") == 0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
_cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
|
||||
int r;
|
||||
@@ -850,6 +915,8 @@ int main(int argc, char *argv[]) {
|
||||
TEST_REQ_RUNNING_SYSTEMD(test_install_printf());
|
||||
test_unit_dump_config_items();
|
||||
test_config_parse_memory_limit();
|
||||
test_contains_instance_specifier_superset();
|
||||
test_unit_is_recursive_template_dependency();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -870,6 +870,22 @@ static void test_unit_name_from_dbus_path(void) {
|
||||
test_unit_name_from_dbus_path_one("/org/freedesktop/systemd1/unit/wpa_5fsupplicant_2eservice", 0, "wpa_supplicant.service");
|
||||
}
|
||||
|
||||
static void test_unit_name_prefix_equal(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
assert_se(unit_name_prefix_equal("a.service", "a.service"));
|
||||
assert_se(unit_name_prefix_equal("a.service", "a.mount"));
|
||||
assert_se(unit_name_prefix_equal("a@b.service", "a.service"));
|
||||
assert_se(unit_name_prefix_equal("a@b.service", "a@c.service"));
|
||||
|
||||
assert_se(!unit_name_prefix_equal("a.service", "b.service"));
|
||||
assert_se(!unit_name_prefix_equal("a.service", "b.mount"));
|
||||
assert_se(!unit_name_prefix_equal("a@a.service", "b.service"));
|
||||
assert_se(!unit_name_prefix_equal("a@a.service", "b@a.service"));
|
||||
assert_se(!unit_name_prefix_equal("a", "b"));
|
||||
assert_se(!unit_name_prefix_equal("a", "a"));
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
_cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
|
||||
int r, rc = 0;
|
||||
@@ -902,6 +918,7 @@ int main(int argc, char* argv[]) {
|
||||
test_unit_name_path_unescape();
|
||||
test_unit_name_to_prefix();
|
||||
test_unit_name_from_dbus_path();
|
||||
test_unit_name_prefix_equal();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user