@@ -121,6 +121,10 @@ static bool supports_filesystem(const char *const filesystem)
121121 if (!inf )
122122 return true;
123123
124+ /* filesystem can be null for bind mounts. */
125+ if (!filesystem )
126+ return true;
127+
124128 len = snprintf (str , sizeof (str ), "nodev\t%s\n" , filesystem );
125129 if (len >= sizeof (str ))
126130 /* Ignores too-long filesystem names. */
@@ -243,7 +247,7 @@ static void prepare_layout_opt(struct __test_metadata *const _metadata,
243247 * for tests relying on pivot_root(2) and move_mount(2).
244248 */
245249 set_cap (_metadata , CAP_SYS_ADMIN );
246- ASSERT_EQ (0 , unshare (CLONE_NEWNS ));
250+ ASSERT_EQ (0 , unshare (CLONE_NEWNS | CLONE_NEWCGROUP ));
247251 ASSERT_EQ (0 , mount_opt (mnt , TMP_DIR ))
248252 {
249253 TH_LOG ("Failed to mount the %s filesystem: %s" , mnt -> type ,
@@ -318,11 +322,13 @@ static void remove_layout1(struct __test_metadata *const _metadata)
318322 EXPECT_EQ (0 , remove_path (file1_s1d3 ));
319323 EXPECT_EQ (0 , remove_path (file1_s1d2 ));
320324 EXPECT_EQ (0 , remove_path (file1_s1d1 ));
325+ EXPECT_EQ (0 , remove_path (dir_s1d3 ));
321326
322327 EXPECT_EQ (0 , remove_path (file2_s2d3 ));
323328 EXPECT_EQ (0 , remove_path (file1_s2d3 ));
324329 EXPECT_EQ (0 , remove_path (file1_s2d2 ));
325330 EXPECT_EQ (0 , remove_path (file1_s2d1 ));
331+ EXPECT_EQ (0 , remove_path (dir_s2d2 ));
326332
327333 EXPECT_EQ (0 , remove_path (file1_s3d1 ));
328334 EXPECT_EQ (0 , remove_path (dir_s3d3 ));
@@ -4482,4 +4488,250 @@ TEST_F_FORK(layout2_overlay, same_content_different_file)
44824488 }
44834489}
44844490
4491+ FIXTURE (layout3_fs )
4492+ {
4493+ bool has_created_dir ;
4494+ bool has_created_file ;
4495+ char * dir_path ;
4496+ bool skip_test ;
4497+ };
4498+
4499+ FIXTURE_VARIANT (layout3_fs )
4500+ {
4501+ const struct mnt_opt mnt ;
4502+ const char * const file_path ;
4503+ };
4504+
4505+ /* clang-format off */
4506+ FIXTURE_VARIANT_ADD (layout3_fs , tmpfs ) {
4507+ /* clang-format on */
4508+ .mnt = mnt_tmp ,
4509+ .file_path = file1_s1d1 ,
4510+ };
4511+
4512+ FIXTURE_VARIANT_ADD (layout3_fs , ramfs ) {
4513+ .mnt = {
4514+ .type = "ramfs" ,
4515+ .data = "mode=700" ,
4516+ },
4517+ .file_path = TMP_DIR "/dir/file" ,
4518+ };
4519+
4520+ FIXTURE_VARIANT_ADD (layout3_fs , cgroup2 ) {
4521+ .mnt = {
4522+ .type = "cgroup2" ,
4523+ },
4524+ .file_path = TMP_DIR "/test/cgroup.procs" ,
4525+ };
4526+
4527+ FIXTURE_VARIANT_ADD (layout3_fs , proc ) {
4528+ .mnt = {
4529+ .type = "proc" ,
4530+ },
4531+ .file_path = TMP_DIR "/self/status" ,
4532+ };
4533+
4534+ FIXTURE_VARIANT_ADD (layout3_fs , sysfs ) {
4535+ .mnt = {
4536+ .type = "sysfs" ,
4537+ },
4538+ .file_path = TMP_DIR "/kernel/notes" ,
4539+ };
4540+
4541+ FIXTURE_SETUP (layout3_fs )
4542+ {
4543+ struct stat statbuf ;
4544+ const char * slash ;
4545+ size_t dir_len ;
4546+
4547+ if (!supports_filesystem (variant -> mnt .type )) {
4548+ self -> skip_test = true;
4549+ SKIP (return , "this filesystem is not supported (setup)" );
4550+ }
4551+
4552+ slash = strrchr (variant -> file_path , '/' );
4553+ ASSERT_NE (slash , NULL );
4554+ dir_len = (size_t )slash - (size_t )variant -> file_path ;
4555+ ASSERT_LT (0 , dir_len );
4556+ self -> dir_path = malloc (dir_len + 1 );
4557+ self -> dir_path [dir_len ] = '\0' ;
4558+ strncpy (self -> dir_path , variant -> file_path , dir_len );
4559+
4560+ prepare_layout_opt (_metadata , & variant -> mnt );
4561+
4562+ /* Creates directory when required. */
4563+ if (stat (self -> dir_path , & statbuf )) {
4564+ set_cap (_metadata , CAP_DAC_OVERRIDE );
4565+ EXPECT_EQ (0 , mkdir (self -> dir_path , 0700 ))
4566+ {
4567+ TH_LOG ("Failed to create directory \"%s\": %s" ,
4568+ self -> dir_path , strerror (errno ));
4569+ free (self -> dir_path );
4570+ self -> dir_path = NULL ;
4571+ }
4572+ self -> has_created_dir = true;
4573+ clear_cap (_metadata , CAP_DAC_OVERRIDE );
4574+ }
4575+
4576+ /* Creates file when required. */
4577+ if (stat (variant -> file_path , & statbuf )) {
4578+ int fd ;
4579+
4580+ set_cap (_metadata , CAP_DAC_OVERRIDE );
4581+ fd = creat (variant -> file_path , 0600 );
4582+ EXPECT_LE (0 , fd )
4583+ {
4584+ TH_LOG ("Failed to create file \"%s\": %s" ,
4585+ variant -> file_path , strerror (errno ));
4586+ }
4587+ EXPECT_EQ (0 , close (fd ));
4588+ self -> has_created_file = true;
4589+ clear_cap (_metadata , CAP_DAC_OVERRIDE );
4590+ }
4591+ }
4592+
4593+ FIXTURE_TEARDOWN (layout3_fs )
4594+ {
4595+ if (self -> skip_test )
4596+ SKIP (return , "this filesystem is not supported (teardown)" );
4597+
4598+ if (self -> has_created_file ) {
4599+ set_cap (_metadata , CAP_DAC_OVERRIDE );
4600+ /*
4601+ * Don't check for error because the file might already
4602+ * have been removed (cf. release_inode test).
4603+ */
4604+ unlink (variant -> file_path );
4605+ clear_cap (_metadata , CAP_DAC_OVERRIDE );
4606+ }
4607+
4608+ if (self -> has_created_dir ) {
4609+ set_cap (_metadata , CAP_DAC_OVERRIDE );
4610+ /*
4611+ * Don't check for error because the directory might already
4612+ * have been removed (cf. release_inode test).
4613+ */
4614+ rmdir (self -> dir_path );
4615+ clear_cap (_metadata , CAP_DAC_OVERRIDE );
4616+ }
4617+ free (self -> dir_path );
4618+ self -> dir_path = NULL ;
4619+
4620+ cleanup_layout (_metadata );
4621+ }
4622+
4623+ static void layer3_fs_tag_inode (struct __test_metadata * const _metadata ,
4624+ FIXTURE_DATA (layout3_fs ) * self ,
4625+ const FIXTURE_VARIANT (layout3_fs ) * variant ,
4626+ const char * const rule_path )
4627+ {
4628+ const struct rule layer1_allow_read_file [] = {
4629+ {
4630+ .path = rule_path ,
4631+ .access = LANDLOCK_ACCESS_FS_READ_FILE ,
4632+ },
4633+ {},
4634+ };
4635+ const struct landlock_ruleset_attr layer2_deny_everything_attr = {
4636+ .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE ,
4637+ };
4638+ const char * const dev_null_path = "/dev/null" ;
4639+ int ruleset_fd ;
4640+
4641+ if (self -> skip_test )
4642+ SKIP (return , "this filesystem is not supported (test)" );
4643+
4644+ /* Checks without Landlock. */
4645+ EXPECT_EQ (0 , test_open (dev_null_path , O_RDONLY | O_CLOEXEC ));
4646+ EXPECT_EQ (0 , test_open (variant -> file_path , O_RDONLY | O_CLOEXEC ));
4647+
4648+ ruleset_fd = create_ruleset (_metadata , LANDLOCK_ACCESS_FS_READ_FILE ,
4649+ layer1_allow_read_file );
4650+ EXPECT_LE (0 , ruleset_fd );
4651+ enforce_ruleset (_metadata , ruleset_fd );
4652+ EXPECT_EQ (0 , close (ruleset_fd ));
4653+
4654+ EXPECT_EQ (EACCES , test_open (dev_null_path , O_RDONLY | O_CLOEXEC ));
4655+ EXPECT_EQ (0 , test_open (variant -> file_path , O_RDONLY | O_CLOEXEC ));
4656+
4657+ /* Forbids directory reading. */
4658+ ruleset_fd =
4659+ landlock_create_ruleset (& layer2_deny_everything_attr ,
4660+ sizeof (layer2_deny_everything_attr ), 0 );
4661+ EXPECT_LE (0 , ruleset_fd );
4662+ enforce_ruleset (_metadata , ruleset_fd );
4663+ EXPECT_EQ (0 , close (ruleset_fd ));
4664+
4665+ /* Checks with Landlock and forbidden access. */
4666+ EXPECT_EQ (EACCES , test_open (dev_null_path , O_RDONLY | O_CLOEXEC ));
4667+ EXPECT_EQ (EACCES , test_open (variant -> file_path , O_RDONLY | O_CLOEXEC ));
4668+ }
4669+
4670+ /* Matrix of tests to check file hierarchy evaluation. */
4671+
4672+ TEST_F_FORK (layout3_fs , tag_inode_dir_parent )
4673+ {
4674+ /* The current directory must not be the root for this test. */
4675+ layer3_fs_tag_inode (_metadata , self , variant , "." );
4676+ }
4677+
4678+ TEST_F_FORK (layout3_fs , tag_inode_dir_mnt )
4679+ {
4680+ layer3_fs_tag_inode (_metadata , self , variant , TMP_DIR );
4681+ }
4682+
4683+ TEST_F_FORK (layout3_fs , tag_inode_dir_child )
4684+ {
4685+ layer3_fs_tag_inode (_metadata , self , variant , self -> dir_path );
4686+ }
4687+
4688+ TEST_F_FORK (layout3_fs , tag_inode_file )
4689+ {
4690+ layer3_fs_tag_inode (_metadata , self , variant , variant -> file_path );
4691+ }
4692+
4693+ /* Light version of layout1.release_inodes */
4694+ TEST_F_FORK (layout3_fs , release_inodes )
4695+ {
4696+ const struct rule layer1 [] = {
4697+ {
4698+ .path = TMP_DIR ,
4699+ .access = LANDLOCK_ACCESS_FS_READ_DIR ,
4700+ },
4701+ {},
4702+ };
4703+ int ruleset_fd ;
4704+
4705+ if (self -> skip_test )
4706+ SKIP (return , "this filesystem is not supported (test)" );
4707+
4708+ /* Clean up for the teardown to not fail. */
4709+ if (self -> has_created_file )
4710+ EXPECT_EQ (0 , remove_path (variant -> file_path ));
4711+
4712+ if (self -> has_created_dir )
4713+ /* Don't check for error because of cgroup specificities. */
4714+ remove_path (self -> dir_path );
4715+
4716+ ruleset_fd =
4717+ create_ruleset (_metadata , LANDLOCK_ACCESS_FS_READ_DIR , layer1 );
4718+ ASSERT_LE (0 , ruleset_fd );
4719+
4720+ /* Unmount the filesystem while it is being used by a ruleset. */
4721+ set_cap (_metadata , CAP_SYS_ADMIN );
4722+ ASSERT_EQ (0 , umount (TMP_DIR ));
4723+ clear_cap (_metadata , CAP_SYS_ADMIN );
4724+
4725+ /* Replaces with a new mount point to simplify FIXTURE_TEARDOWN. */
4726+ set_cap (_metadata , CAP_SYS_ADMIN );
4727+ ASSERT_EQ (0 , mount_opt (& mnt_tmp , TMP_DIR ));
4728+ clear_cap (_metadata , CAP_SYS_ADMIN );
4729+
4730+ enforce_ruleset (_metadata , ruleset_fd );
4731+ ASSERT_EQ (0 , close (ruleset_fd ));
4732+
4733+ /* Checks that access to the new mount point is denied. */
4734+ ASSERT_EQ (EACCES , test_open (TMP_DIR , O_RDONLY ));
4735+ }
4736+
44854737TEST_HARNESS_MAIN
0 commit comments