VFS vn_lock function makes system unresponsive when calling vn_fullpath

Alexander Morozov alex at nixd.org
Tue May 30 15:04:34 UTC 2017


Hi,

At the moment I am developing a kernel module based on MAC Framework 
which is invoking a vn_fullpath() from vfs_cache.c.

Here is the thing:
When the MAC Framework mpo_vnode_check_write procedure is called, the 
kernel module is trying to retrieve: the path on the disk for the 
curthread and the path of the file in which the curthread is attempting 
to write. At the point when the program execution reaches the 
vn_fullpath() for the resolution of the file's path, the terminal window 
'freezes' (actually the whole system is not responding, the rest ttys 
stop responding after entering login credentials).

For instance, after loading and initializing a MAC kernel module, I am 
opening a existing text file using vi  to edit it. After inserting some 
random text I press ESC key on keyboard  and terminal window 'freezes' 
(at that moment the  mpo_vnode_check_write is called) ( the struct vnode 
* vp which is passed to the MAC procedure is valid (not NULL) and type 
(enum vtype) is VREG).

I have investigated this issue and found out the following:
The get_fullpath() is calling the get_fullpath1() where later  the 
vn_vptocnp() is invoked.
Retrieving the location for the curthread is successful (the full path 
returned).
But when the kernel module is making attempt to retrieve the path for 
the vp (argument of the mpo_vnode_check_write) the function vn_lock(*vp, 
LK_SHARED | LK_RETRY) (line 2191) located in function vn_vptocnp() is 
grabbing control forever.
Running the kernel in debug mode showed that (in the case with vi 
described above) the condition check if (ncp != NULL)  (line 2167) 
fails, the ncp is NULL. In such case the program exec goes further, 
continuing from SDT_PROBE1(vfs, namecache, fullpath, miss, vp); (line 
2188). When vn_lock (line 2191) is entered, it can't return from vn_lock 
for some reason. Referring to the DDB tracing, the code can't lock vp 
(vnode) and as a result loop this operation. It seems that LK_RETRY does 
not allow to return from function without locking vnode instance 
otherwise it would simply return error code.

The trace:
VOP_LOCK1__APV()
_vn_lock()
_vn_vptocnp_locked()
(Sometimes the DDB stops breaking execution after requesting 's' single 
step and I can't investigate the problem any further, need to restart 
the VM. This time it stoped at VOP_LOCK_APV+0x11: testq %rdi, %rdi)

I am trying to figure out why the system can't lock it. It seems that 
the file either does not exist, so there is nothing to lock or some 
exclusive lock was applied (I will check it later).
Any ideas or comments on this problem are welcomed!

The same problem when I am closing the the mc-light (midnight 
commander). The OS is running as the guest in the VrtualBox (single 
core, 1024MB RAM).

uname -a
FreeBSD  11.0-RELEASE-p10 FreeBSD 11.0-RELEASE-p10 #0 r318316M: Tue May 
30 07:41:36 UTC 2017     root@:/usr/obj/usr/src/sys/MYKERN  amd64

The kernel was rebuild with additional option (options DDB).

Below I have copied and pasted the code which performers the path 
resolution and the MAC procedure handler:
static int
rw_retreive_data(struct thread * td, struct vnode *dvp, char ** rpath, 
char ** curpath, struct sandbox_rule_app ** rule_ptr)
{
	ASSERT_NULL_R(dvp, 	-1);
	ASSERT_NULL_R(rpath, 	-1);
	ASSERT_NULL_R(curpath, 	-1);
	ASSERT_NULL_R(rule_ptr,      -1);

	char * fpath 		= NULL;
	char * curfreepath = NULL;
	int error 			= 0;

	error = vn_fullpath(td, td->td_proc->p_textvp, curpath, &curfreepath);
	if (error != 0)
	{
		 printf("sandbox: %s error! vn_fullback returned [%d]\r\n", 
__FUNCTION__, error);
		 return -1;
	}
	uprintf("1: %s path: %s\r\n", __FUNCTION__, *curpath);

	uprintf("2: %s path: vtype %d v_iflag %u v_vflag %u\r\n", __FUNCTION__, 
dvp->v_type, dvp->v_iflag, dvp->v_vflag);

	error = vn_fullpath(td, dvp, rpath, &fpath);
	if (error != 0)
	{
                  //handle errorno there before returning
		 printf("sandbox: %s error! vn_fullback returned [%d]\r\n", 
__FUNCTION__, error);
		 error = ENOENT;
		 goto leave;
	}

	//uprintf("2: %s path: %s vtype %d v_iflag %u v_vflag %u\r\n", 
__FUNCTION__, *rpath, dvp->v_type, dvp->v_iflag, dvp->v_vflag);

	if (rules_lookup_app_rule_bypath(*curpath, rule_ptr) != 0)
	{
		//not found, return -1 because the exec path was found, but the 
operated file can not be found (cache purged???)
		printf("sandbox: %s Rule was not found in rule list for [%s].\r\n", 
__FUNCTION__, *curpath);
		error = -1;
	}

leave:
	if (fpath != NULL)
	{
		KFREE((void*) fpath);
	}

	if (curfreepath != NULL)
	{
		KFREE((void*) curfreepath);
	}

	return error;
}

int sandbox_vnode_check_write(struct ucred *active_cred,
								struct ucred *file_cred,
								struct vnode *vp,
								struct label *vplabel)
{
	IS_MODULE_INITED(0)
	ASSERT_NULL_R(vp, 0);

	struct sandbox_rule_app * rule_ptr = NULL;
	char * rpath = "-";
	char * curpath = "-";
	int error = 0;

	RWLOCK_BLOCK(&sandbox_rules_lock, RWLOCK_READ)
	{

		error = rw_retreive_data(curthread, vp, &rpath, &curpath, &rule_ptr);

		if (error == 0)
		{
			//printf("--> sandbox: debug, writing to %s\r\n", rpath);

				error = sandboxing_check_filewritedata(rule_ptr, rpath);

		}
		else
		{
			printf("debug_write %s %s\r\n", rpath, curpath);
			error = get_rule_notfound_policy(error);
		}

	} //RWLOCK_BLOCK

	return (error);
}


-- 
Kind Regards,
Alexander Morozov


More information about the freebsd-fs mailing list