@@ -2612,6 +2612,60 @@ TEST_F_FORK(layout1, refer_denied_by_default4)
layer_dir_s1d1_refer);
}
+/*
+ * Regression test for layered domains: The two enforced Landlock layers both do
+ * not restrict the tested "refer" operations on their own, so they should also
+ * not restrict them when they are layered, regardless of the layer order.
+ */
+static void test_layering_does_not_break_refer(
+ struct __test_metadata *const _metadata,
+ const struct landlock_ruleset_attr *const non_fs_attr,
+ const bool non_fs_first)
+{
+ ASSERT_EQ(0, unlink(file1_s1d2));
+
+ if (non_fs_first)
+ drop_access_rights(_metadata, non_fs_attr);
+
+ enforce_fs(_metadata, LANDLOCK_ACCESS_FS_REFER, layer_dir_s1d1_refer);
+
+ if (!non_fs_first)
+ drop_access_rights(_metadata, non_fs_attr);
+
+ EXPECT_EQ(0, test_rename(file1_s1d1, file1_s1d2));
+ EXPECT_EQ(0, test_rename(file1_s1d2, file1_s1d1));
+ EXPECT_EQ(0, test_exchange(file2_s1d1, file2_s1d2));
+ EXPECT_EQ(0, test_exchange(file2_s1d2, file2_s1d1));
+}
+
+static const struct landlock_ruleset_attr net_attr = {
+ .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP,
+};
+
+static const struct landlock_ruleset_attr scoped_attr = {
+ .scoped = LANDLOCK_SCOPE_SIGNAL,
+};
+
+TEST_F_FORK(layout1, layering_fs_then_net_does_not_break_refer)
+{
+ test_layering_does_not_break_refer(_metadata, &net_attr, false);
+}
+
+TEST_F_FORK(layout1, layering_net_then_fs_does_not_break_refer)
+{
+ test_layering_does_not_break_refer(_metadata, &net_attr, true);
+}
+
+TEST_F_FORK(layout1, layering_fs_then_scoped_does_not_break_refer)
+{
+ test_layering_does_not_break_refer(_metadata, &scoped_attr, false);
+}
+
+TEST_F_FORK(layout1, layering_scoped_then_fs_does_not_break_refer)
+{
+ test_layering_does_not_break_refer(_metadata, &scoped_attr, true);
+}
+
/*
* Tests walking through a denied root mount.
*/