Skip to content

9pfs without caching creates an inode for each access, breaking landlock #45

@micromaomao

Description

@micromaomao

Hi,

Previously, I noticed that when using 9pfs filesystems, landlock is blocking access even for directories allowed by rules, and that this has something to do with 9pfs creating new inodes despite the existing one being held by landlock:

root@1af6e5dc0b82 ~# env LL_FS_RO=/etc:/usr:/bin:/lib LL_FS_RW= /sandboxer bash
Executing the sandboxed command...
bash: /etc/bash.bashrc: Permission denied
bash-5.2# ls -la /etc
ls: cannot open directory '/etc': Permission denied
bash-5.2# 
exit
root@1af6e5dc0b82 ~ [2]# cd /etc # keep /etc open, somehow
root@1af6e5dc0b82 /etc# env LL_FS_RO=/etc:/usr:/bin:/lib LL_FS_RW= /sandboxer bash
Executing the sandboxed command...
root@1af6e5dc0b82:/etc# ls -la /etc | head
total 372
drwxr-xr-x 65 root root    2620 Mar  2 23:41 .
drwxrwxrwt 19 root root     520 Mar  2 23:45 ..
-rw-------  1 root root       0 Feb 24 00:00 .pwd.lock
drwxr-xr-x  3 root root      60 Mar  1 21:54 X11
-rw-r--r--  1 root root    3040 May 25  2023 adduser.conf
drwxr-xr-x  2 root root    1940 Mar  1 21:55 alternatives
drwxr-xr-x  3 root root     100 Mar  1 21:55 apparmor.d
drwxr-xr-x  8 root root     160 Feb 24 00:00 apt
-rw-r--r--  1 root root    1994 Mar 29  2024 bash.bashrc
root@1af6e5dc0b82:/etc#  
exit
root@1af6e5dc0b82 /etc# mount | grep 9p
root on / type 9p (rw,relatime,access=client,trans=virtio)

I've just did slightly more investigation on this yesterday. On next-20250326, it's still happening. However, there is this curious bit in v9fs_vfs_lookup:

struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
				      unsigned int flags)
{
	...

	/*
	 * Make sure we don't use a wrong inode due to parallel
	 * unlink. For cached mode create calls request for new
	 * inode. But with cache disabled, lookup should do this.
	 */
	name = dentry->d_name.name;
	fid = p9_client_walk(dfid, 1, &name, 1);
	p9_fid_put(dfid);
	if (fid == ERR_PTR(-ENOENT))
		inode = NULL;
	else if (IS_ERR(fid))
		inode = ERR_CAST(fid);
	else if (v9ses->cache & (CACHE_META|CACHE_LOOSE))
		inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb);
	else
		inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);

Testing with cache=loose added to the mount option, this problem disappears/

At least when using cache, 9pfs does do an iget5_locked with a sensible test function (v9fs_test_inode_dotl). I'm not sure what is the reasoning behind creating a new inode for each access in uncached node (given that after @l0kod 's fix, hostfs no longer does this, and presumably can still see up-to-date information from the host? (not tested, and not really knowledgeable in these area)

Hopefully there is a way to do a similar fix for 9pfs without breaking too much prior assumptions.

(I haven't posted this to the mailing list yet but I should probably do so - after a bit more searching)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions