Index: sys/kern/kern_jail.c =================================================================== --- sys/kern/kern_jail.c (revision 229855) +++ sys/kern/kern_jail.c (working copy) @@ -531,6 +531,7 @@ kern_jail_set(struct thread *td, struct uio *optui int gotchildmax, gotenforce, gothid, gotslevel; int fi, jid, jsys, len, level; int childmax, slevel, vfslocked; + int fullpath_disabled = 0; #if defined(INET) || defined(INET6) int ii, ij; #endif @@ -897,30 +898,42 @@ kern_jail_set(struct thread *td, struct uio *optui error = EINVAL; goto done_free; } - if (len < 2 || (len == 2 && path[0] == '/')) - path = NULL; - else { + NDINIT(&nd, LOOKUP, + FOLLOW | LOCKLEAF | MPSAFE, UIO_SYSSPACE, + path, td); + error = namei(&nd); + if (error) + goto done_free; + vfslocked = NDHASGIANT(&nd); + root = nd.ni_vp; + NDFREE(&nd, NDF_ONLY_PNBUF); + error = vn_path_to_global_path(td, root, path, + MAXPATHLEN); + if (error == ENODEV) { + /* proceed if sysctl debug.disablefullpath == 1 */ + fullpath_disabled = 1; + if (len < 2 || (len == 2 && path[0] == '/')) + path = NULL; + } else if (error != 0) { + /* exit on other errors */ + VFS_UNLOCK_GIANT(vfslocked); + goto done_free; + } + VOP_UNLOCK(root, 0); + if (root->v_type != VDIR) { + error = ENOTDIR; + vrele(root); + VFS_UNLOCK_GIANT(vfslocked); + goto done_free; + } + VFS_UNLOCK_GIANT(vfslocked); + if (fullpath_disabled) { /* Leave room for a real-root full pathname. */ if (len + (path[0] == '/' && strcmp(mypr->pr_path, "/") ? strlen(mypr->pr_path) : 0) > MAXPATHLEN) { error = ENAMETOOLONG; goto done_free; } - NDINIT(&nd, LOOKUP, MPSAFE | FOLLOW, UIO_SYSSPACE, - path, td); - error = namei(&nd); - if (error) - goto done_free; - vfslocked = NDHASGIANT(&nd); - root = nd.ni_vp; - NDFREE(&nd, NDF_ONLY_PNBUF); - if (root->v_type != VDIR) { - error = ENOTDIR; - vrele(root); - VFS_UNLOCK_GIANT(vfslocked); - goto done_free; - } - VFS_UNLOCK_GIANT(vfslocked); } } @@ -1583,7 +1596,8 @@ kern_jail_set(struct thread *td, struct uio *optui } if (path != NULL) { /* Try to keep a real-rooted full pathname. */ - if (path[0] == '/' && strcmp(mypr->pr_path, "/")) + if (fullpath_disabled && + path[0] == '/' && strcmp(mypr->pr_path, "/")) snprintf(pr->pr_path, sizeof(pr->pr_path), "%s%s", mypr->pr_path, path); else Index: sys/kern/vfs_mount.c =================================================================== --- sys/kern/vfs_mount.c (revision 229855) +++ sys/kern/vfs_mount.c (working copy) @@ -1085,11 +1085,14 @@ vfs_domount( NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; if ((fsflags & MNT_UPDATE) == 0) { - error = vfs_domount_first(td, vfsp, fspath, vp, fsflags, - optlist); - } else { + error = vn_path_to_global_path(td, vp, fspath, MNAMELEN); + /* debug.disablefullpath == 1 results in ENODEV */ + if (error == 0 || error == ENODEV) { + error = vfs_domount_first(td, vfsp, fspath, vp, + fsflags, optlist); + } + } else error = vfs_domount_update(td, vp, fsflags, optlist); - } mtx_unlock(&Giant); ASSERT_VI_UNLOCKED(vp, __func__); @@ -1120,8 +1123,9 @@ sys_unmount(td, uap) } */ *uap; { struct mount *mp; + struct nameidata nd; char *pathbuf; - int error, id0, id1; + int error, id0, id1, vfslocked; AUDIT_ARG_VALUE(uap->flags); if (jailed(td->td_ucred) || usermount == 0) { @@ -1155,6 +1159,24 @@ sys_unmount(td, uap) mtx_unlock(&mountlist_mtx); } else { AUDIT_ARG_UPATH1(td, pathbuf); + /* + * Try to find global path for path argument. + */ + NDINIT(&nd, LOOKUP, + FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1, + UIO_SYSSPACE, pathbuf, td); + if (namei(&nd) == 0) { + if (NDHASGIANT(&nd)) + mtx_unlock(&Giant); + NDFREE(&nd, NDF_ONLY_PNBUF); + error = vn_path_to_global_path(td, nd.ni_vp, + pathbuf, MNAMELEN); + if (error == 0 || error == ENODEV) { + vfslocked = VFS_LOCK_GIANT(nd.ni_vp->vp_mount); + vput(nd.ni_vp); + VFS_UNLOCK_GIANT(vfslocked); + } + } mtx_lock(&mountlist_mtx); TAILQ_FOREACH_REVERSE(mp, &mountlist, mntlist, mnt_list) { if (strcmp(mp->mnt_stat.f_mntonname, pathbuf) == 0) Index: sys/kern/vfs_cache.c =================================================================== --- sys/kern/vfs_cache.c (revision 229855) +++ sys/kern/vfs_cache.c (working copy) @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -1278,3 +1279,84 @@ vn_commname(struct vnode *vp, char *buf, u_int buf buf[l] = '\0'; return (0); } + +/* + * This function updates path string to vnode's full global path + * and checks maximum allowed path size against pathlen. + * + * Requires a locked and referenced vnode. + * Vnode is re-locked on success or ENODEV, otherwise unlocked. + * + * If sysctl debug.disablefullpath is set, ENODEV is returned, + * vnode is re-locked and path remain untouched. + */ +int +vn_path_to_global_path(struct thread *td, struct vnode *vp, char *path, + u_int pathlen) +{ + struct nameidata nd; + struct vnode *vp1; + char *rpath, *fbuf; + int error, vfslocked; + + ASSERT_VOP_ELOCKED(vp, __func__); + + /* Return ENODEV if sysctl debug.disablefullpath==1 */ + if (disablefullpath) + return (ENODEV); + + /* Construct global filesystem path from vp. */ + VOP_UNLOCK(vp, 0); + error = vn_fullpath_global(td, vp, &rpath, &fbuf); + + if (error != 0) { + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + vrele(vp); + VFS_UNLOCK_GIANT(vfslocked); + return (error); + } + + if (strlen(rpath) >= pathlen) { + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + vrele(vp); + VFS_UNLOCK_GIANT(vfslocked); + error = ENAMETOOLONG; + goto out; + } + + /* + * Re-lookup the vnode by path to detect a possible rename. + * As a side effect, the vnode is relocked. + * If vnode was renamed, return ENOENT. + */ + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1, + UIO_SYSSPACE, path, td); + error = namei(&nd); + if (error != 0) { + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + vrele(vp); + VFS_UNLOCK_GIANT(vfslocked); + goto out; + } + + if (NDHASGIANT(&nd)) + mtx_unlock(&Giant); + NDFREE(&nd, NDF_ONLY_PNBUF); + vp1 = nd.ni_vp; + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + vrele(vp); + VFS_UNLOCK_GIANT(vfslocked); + if (vp1 != vp) { + vfslocked = VFS_LOCK_GIANT(vp1->v_mount); + vput(vp1); + VFS_UNLOCK_GIANT(vfslocked); + error = ENOENT; + goto out; + } + + strcpy(path, rpath); + +out: + free(fbuf, M_TEMP); + return (error); +} Index: sys/sys/vnode.h =================================================================== --- sys/sys/vnode.h (revision 229855) +++ sys/sys/vnode.h (working copy) @@ -605,6 +605,8 @@ int vn_fullpath(struct thread *td, struct vnode *v int vn_fullpath_global(struct thread *td, struct vnode *vn, char **retbuf, char **freebuf); int vn_commname(struct vnode *vn, char *buf, u_int buflen); +int vn_path_to_global_path(struct thread *td, struct vnode *vp, + char *path, u_int pathlen); int vaccess(enum vtype type, mode_t file_mode, uid_t file_uid, gid_t file_gid, accmode_t accmode, struct ucred *cred, int *privused);