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