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