svn commit: r231692 - in stable/9/sys: kern sys

Martin Matuska mm at FreeBSD.org
Tue Feb 14 17:09:21 UTC 2012


Author: mm
Date: Tue Feb 14 17:09:20 2012
New Revision: 231692
URL: http://svn.freebsd.org/changeset/base/231692

Log:
  MFC r230129,r230143,r230407,r231012:
  
  MFC r320129 [1]:
  Introduce vn_path_to_global_path()
  
  This function updates path string to vnode's full global path and checks
  the size of the new path string against the pathlen argument.
  
  In vfs_domount(), sys_unmount() and kern_jail_set() this new function
  is used to update the supplied path argument to the respective global path.
  
  Unbreaks jailed zfs(8) with enforce_statfs set to 1.
  
  MFC r230143 [2]:
  Fix missing in r230129:
  
  kern_jail.c: initialize fullpath_disabled to zero
  vfs_cache.c: add missing dot in comment
  
  MFC r230407 [3]:
  Use separate buffer for global path to avoid overflow of path buffer.
  
  MFC r231012:
  Analogous to r230407 a separate path buffer in vfs_mount.c is required
  for r230129. Fixes a out of bounds write to fspath.
  
  Reviewed by:	kib [1] [2], jamie [3]

Modified:
  stable/9/sys/kern/kern_jail.c
  stable/9/sys/kern/vfs_cache.c
  stable/9/sys/kern/vfs_mount.c
  stable/9/sys/sys/vnode.h
Directory Properties:
  stable/9/sys/   (props changed)

Modified: stable/9/sys/kern/kern_jail.c
==============================================================================
--- stable/9/sys/kern/kern_jail.c	Tue Feb 14 16:47:52 2012	(r231691)
+++ stable/9/sys/kern/kern_jail.c	Tue Feb 14 17:09:20 2012	(r231692)
@@ -521,6 +521,7 @@ kern_jail_set(struct thread *td, struct 
 	struct prison *pr, *deadpr, *mypr, *ppr, *tpr;
 	struct vnode *root;
 	char *domain, *errmsg, *host, *name, *namelc, *p, *path, *uuid;
+	char *g_path;
 #if defined(INET) || defined(INET6)
 	struct prison *tppr;
 	void *op;
@@ -531,6 +532,7 @@ kern_jail_set(struct thread *td, struct 
 	int gotchildmax, gotenforce, gothid, gotslevel;
 	int fi, jid, jsys, len, level;
 	int childmax, slevel, vfslocked;
+	int fullpath_disabled;
 #if defined(INET) || defined(INET6)
 	int ii, ij;
 #endif
@@ -574,6 +576,7 @@ kern_jail_set(struct thread *td, struct 
 #ifdef INET6
 	ip6 = NULL;
 #endif
+	g_path = NULL;
 
 	error = vfs_copyopt(opts, "jid", &jid, sizeof(jid));
 	if (error == ENOENT)
@@ -880,6 +883,7 @@ kern_jail_set(struct thread *td, struct 
 	}
 #endif
 
+	fullpath_disabled = 0;
 	root = NULL;
 	error = vfs_getopt(opts, "path", (void **)&path, &len);
 	if (error == ENOENT)
@@ -897,30 +901,44 @@ kern_jail_set(struct thread *td, struct 
 			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);
+		g_path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
+		strlcpy(g_path, path, MAXPATHLEN);
+		error = vn_path_to_global_path(td, root, g_path, MAXPATHLEN);
+		if (error == 0)
+			path = g_path;
+		else if (error == ENODEV) {
+			/* proceed if sysctl debug.disablefullpath == 1 */
+			fullpath_disabled = 1;
+			if (len < 2 || (len == 2 && path[0] == '/'))
+				path = NULL;
+		} else {
+			/* exit on other errors */
+			VFS_UNLOCK_GIANT(vfslocked);
+			goto done_free;
+		}
+		if (root->v_type != VDIR) {
+			error = ENOTDIR;
+			vput(root);
+			VFS_UNLOCK_GIANT(vfslocked);
+			goto done_free;
+		}
+		VOP_UNLOCK(root, 0);
+		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 +1601,8 @@ kern_jail_set(struct thread *td, struct 
 	}
 	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
@@ -1806,6 +1825,8 @@ kern_jail_set(struct thread *td, struct 
 #ifdef INET6
 	free(ip6, M_PRISON);
 #endif
+	if (g_path != NULL)
+		free(g_path, M_TEMP);
 	vfs_freeopts(opts);
 	return (error);
 }

Modified: stable/9/sys/kern/vfs_cache.c
==============================================================================
--- stable/9/sys/kern/vfs_cache.c	Tue Feb 14 16:47:52 2012	(r231691)
+++ stable/9/sys/kern/vfs_cache.c	Tue Feb 14 17:09:20 2012	(r231692)
@@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/malloc.h>
+#include <sys/fcntl.h>
 #include <sys/mount.h>
 #include <sys/namei.h>
 #include <sys/proc.h>
@@ -1277,3 +1278,76 @@ vn_commname(struct vnode *vp, char *buf,
 	buf[l] = '\0';
 	return (0);
 }
+
+/*
+ * This function updates path string to vnode's full global path
+ * and checks the size of the new path string against the pathlen argument.
+ *
+ * Requires a locked, referenced vnode and GIANT lock held.
+ * Vnode is re-locked on success or ENODEV, otherwise unlocked.
+ *
+ * If sysctl debug.disablefullpath is set, ENODEV is returned,
+ * vnode is left locked and path remain untouched.
+ *
+ * If vp is a directory, the call to vn_fullpath_global() always succeeds
+ * because it falls back to the ".." lookup if the namecache lookup fails.
+ */
+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;
+
+	VFS_ASSERT_GIANT(vp->v_mount);
+	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) {
+		vrele(vp);
+		return (error);
+	}
+
+	if (strlen(rpath) >= pathlen) {
+		vrele(vp);
+		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) {
+		vrele(vp);
+		goto out;
+	}
+	vfslocked = NDHASGIANT(&nd);
+	NDFREE(&nd, NDF_ONLY_PNBUF);
+	vp1 = nd.ni_vp;
+	vrele(vp);
+	if (vp1 == vp)
+		strcpy(path, rpath);
+	else {
+		vput(vp1);
+		error = ENOENT;
+	}
+	VFS_UNLOCK_GIANT(vfslocked);
+
+out:
+	free(fbuf, M_TEMP);
+	return (error);
+}

Modified: stable/9/sys/kern/vfs_mount.c
==============================================================================
--- stable/9/sys/kern/vfs_mount.c	Tue Feb 14 16:47:52 2012	(r231691)
+++ stable/9/sys/kern/vfs_mount.c	Tue Feb 14 17:09:20 2012	(r231692)
@@ -1024,6 +1024,7 @@ vfs_domount(
 	struct vfsconf *vfsp;
 	struct nameidata nd;
 	struct vnode *vp;
+	char *pathbuf;
 	int error;
 
 	/*
@@ -1087,11 +1088,17 @@ 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 {
+		pathbuf = malloc(MNAMELEN, M_TEMP, M_WAITOK);
+		strcpy(pathbuf, fspath);
+		error = vn_path_to_global_path(td, vp, pathbuf, MNAMELEN);
+		/* debug.disablefullpath == 1 results in ENODEV */
+		if (error == 0 || error == ENODEV) {
+			error = vfs_domount_first(td, vfsp, pathbuf, vp,
+			    fsflags, optlist);
+		}
+		free(pathbuf, M_TEMP);
+	} else
 		error = vfs_domount_update(td, vp, fsflags, optlist);
-	}
 	mtx_unlock(&Giant);
 
 	ASSERT_VI_UNLOCKED(vp, __func__);
@@ -1121,9 +1128,10 @@ sys_unmount(td, uap)
 		int flags;
 	} */ *uap;
 {
+	struct nameidata nd;
 	struct mount *mp;
 	char *pathbuf;
-	int error, id0, id1;
+	int error, id0, id1, vfslocked;
 
 	AUDIT_ARG_VALUE(uap->flags);
 	if (jailed(td->td_ucred) || usermount == 0) {
@@ -1157,6 +1165,21 @@ 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) {
+			vfslocked = NDHASGIANT(&nd);
+			NDFREE(&nd, NDF_ONLY_PNBUF);
+			error = vn_path_to_global_path(td, nd.ni_vp, pathbuf,
+			    MNAMELEN);
+			if (error == 0 || error == ENODEV)
+				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)

Modified: stable/9/sys/sys/vnode.h
==============================================================================
--- stable/9/sys/sys/vnode.h	Tue Feb 14 16:47:52 2012	(r231691)
+++ stable/9/sys/sys/vnode.h	Tue Feb 14 17:09:20 2012	(r231692)
@@ -605,6 +605,8 @@ int	vn_fullpath(struct thread *td, struc
 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);


More information about the svn-src-stable-9 mailing list