lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:	Fri, 04 Jun 2010 13:52:12 +0200
From:	Miklos Szeredi <miklos@...redi.hu>
To:	Al Viro <viro@...IV.linux.org.uk>,
	Andrew Morton <akpm@...ux-foundation.org>
CC:	John Johansen <john.johansen@...onical.com>,
	Tetsuo Handa <penguin-kernel@...ove.SAKURA.ne.jp>,
	linux-fsdevel@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: [PATCH 1/2] vfs: sanitize __d_path()

From: Miklos Szeredi <mszeredi@...e.cz>

__d_path() no longer appends " (deleted)" to unlinked paths.  This is
moved into d_path() which is the only caller that cares.

If a global root is reached then __d_path() no longer prepends the
name of the root dentry.  This was needed by pseudo filesystems, but
the d_dname() method has superseded it.

Signed-off-by: Miklos Szeredi <mszeredi@...e.cz>
---
 fs/dcache.c                |   99 ++++++++++++++++++++-------------------------
 fs/seq_file.c              |   12 +++--
 include/linux/dcache.h     |    2 
 security/tomoyo/realpath.c |   17 ++++---
 4 files changed, 62 insertions(+), 68 deletions(-)

Index: linux-2.6/fs/dcache.c
===================================================================
--- linux-2.6.orig/fs/dcache.c	2010-05-27 12:13:46.000000000 +0200
+++ linux-2.6/fs/dcache.c	2010-05-27 12:22:21.000000000 +0200
@@ -1904,78 +1904,57 @@ static int prepend_name(char **buffer, i
  * __d_path - return the path of a dentry
  * @path: the dentry/vfsmount to report
  * @root: root vfsmnt/dentry (may be modified by this function)
- * @buffer: buffer to return value in
- * @buflen: buffer length
+ * @buffer: end of free buffer, will point to the result on return
+ * @buflen: pointer to buffer length
  *
- * Convert a dentry into an ASCII path name. If the entry has been deleted
- * the string " (deleted)" is appended. Note that this is ambiguous.
+ * Convert a vfsmount+dentry into an ASCII path name.
  *
- * Returns a pointer into the buffer or an error code if the
- * path was too long.
+ * Returns an error code if the path was too long.
  *
  * "buflen" should be positive. Caller holds the dcache_lock.
  *
  * If path is not reachable from the supplied root, then the value of
  * root is changed (without modifying refcounts).
  */
-char *__d_path(const struct path *path, struct path *root,
-	       char *buffer, int buflen)
+int __d_path(const struct path *path, struct path *root,
+	     char **buffer, int *buflen)
 {
 	struct dentry *dentry = path->dentry;
 	struct vfsmount *vfsmnt = path->mnt;
-	char *end = buffer + buflen;
-	char *retval;
+	bool slash = false;
+	int error = 0;
 
 	spin_lock(&vfsmount_lock);
-	prepend(&end, &buflen, "\0", 1);
-	if (d_unlinked(dentry) &&
-		(prepend(&end, &buflen, " (deleted)", 10) != 0))
-			goto Elong;
-
-	if (buflen < 1)
-		goto Elong;
-	/* Get '/' right */
-	retval = end-1;
-	*retval = '/';
-
-	for (;;) {
-		struct dentry * parent;
-
-		if (dentry == root->dentry && vfsmnt == root->mnt)
-			break;
+	while (dentry != root->dentry || vfsmnt != root->mnt) {
 		if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
 			/* Global root? */
 			if (vfsmnt->mnt_parent == vfsmnt) {
-				goto global_root;
+				root->mnt = vfsmnt;
+				root->dentry = dentry;
+				break;
 			}
 			dentry = vfsmnt->mnt_mountpoint;
 			vfsmnt = vfsmnt->mnt_parent;
-			continue;
+		} else {
+			struct dentry *parent = dentry->d_parent;
+
+			prefetch(parent);
+			error = prepend_name(buffer, buflen, &dentry->d_name);
+			if (!error)
+				error = prepend(buffer, buflen, "/", 1);
+			if (error)
+				break;
+
+			slash = true;
+			dentry = parent;
 		}
-		parent = dentry->d_parent;
-		prefetch(parent);
-		if ((prepend_name(&end, &buflen, &dentry->d_name) != 0) ||
-		    (prepend(&end, &buflen, "/", 1) != 0))
-			goto Elong;
-		retval = end;
-		dentry = parent;
 	}
-
-out:
 	spin_unlock(&vfsmount_lock);
-	return retval;
 
-global_root:
-	retval += 1;	/* hit the slash */
-	if (prepend_name(&retval, &buflen, &dentry->d_name) != 0)
-		goto Elong;
-	root->mnt = vfsmnt;
-	root->dentry = dentry;
-	goto out;
+	if (!error && !slash)
+		error = prepend(buffer, buflen, "/", 1);
 
-Elong:
-	retval = ERR_PTR(-ENAMETOOLONG);
-	goto out;
+	return error;
 }
 
 /**
@@ -1996,9 +1975,10 @@ Elong:
  */
 char *d_path(const struct path *path, char *buf, int buflen)
 {
-	char *res;
+	char *ptr;
 	struct path root;
 	struct path tmp;
+	int error;
 
 	/*
 	 * We have various synthetic filesystems that never get mounted.  On
@@ -2015,11 +1995,19 @@ char *d_path(const struct path *path, ch
 	path_get(&root);
 	read_unlock(&current->fs->lock);
 	spin_lock(&dcache_lock);
+	ptr = buf + buflen;
+	prepend(&ptr, &buflen, "\0", 1);
+	if (d_unlinked(path->dentry)) {
+		error = prepend(&ptr, &buflen, " (deleted)", 10);
+		if (error)
+			goto out;
+	}
 	tmp = root;
-	res = __d_path(path, &tmp, buf, buflen);
+	error = __d_path(path, &tmp, &ptr, &buflen);
+out:
 	spin_unlock(&dcache_lock);
 	path_put(&root);
-	return res;
+	return error ? ERR_PTR(error) : ptr;
 }
 EXPORT_SYMBOL(d_path);
 
@@ -2120,13 +2108,14 @@ SYSCALL_DEFINE2(getcwd, char __user *, b
 	if (!d_unlinked(pwd.dentry)) {
 		unsigned long len;
 		struct path tmp = root;
-		char * cwd;
+		int buflen = PAGE_SIZE;
+		char *cwd = page + buflen;
 
-		cwd = __d_path(&pwd, &tmp, page, PAGE_SIZE);
+		prepend(&cwd, &buflen, "\0", 1);
+		error = __d_path(&pwd, &tmp, &cwd, &buflen);
 		spin_unlock(&dcache_lock);
 
-		error = PTR_ERR(cwd);
-		if (IS_ERR(cwd))
+		if (error)
 			goto out;
 
 		error = -ERANGE;
Index: linux-2.6/fs/seq_file.c
===================================================================
--- linux-2.6.orig/fs/seq_file.c	2010-05-27 12:13:46.000000000 +0200
+++ linux-2.6/fs/seq_file.c	2010-05-27 12:13:48.000000000 +0200
@@ -448,7 +448,8 @@ int seq_path(struct seq_file *m, struct
 EXPORT_SYMBOL(seq_path);
 
 /*
- * Same as seq_path, but relative to supplied root.
+ * Same as seq_path, but relative to supplied root and doesn't append
+ * " (deleted)" for unlinked dentries.
  *
  * root may be changed, see __d_path().
  */
@@ -460,13 +461,14 @@ int seq_path_root(struct seq_file *m, st
 	int res = -ENAMETOOLONG;
 
 	if (size) {
-		char *p;
+		int buflen = size - 1;
+		char *p = buf + buflen;
 
+		*p = '\0';
 		spin_lock(&dcache_lock);
-		p = __d_path(path, root, buf, size);
+		res = __d_path(path, root, &p, &buflen);
 		spin_unlock(&dcache_lock);
-		res = PTR_ERR(p);
-		if (!IS_ERR(p)) {
+		if (!res) {
 			char *end = mangle_path(buf, p, esc);
 			if (end)
 				res = end - buf;
Index: linux-2.6/include/linux/dcache.h
===================================================================
--- linux-2.6.orig/include/linux/dcache.h	2010-05-27 12:13:46.000000000 +0200
+++ linux-2.6/include/linux/dcache.h	2010-05-27 12:13:48.000000000 +0200
@@ -313,7 +313,7 @@ extern int d_validate(struct dentry *, s
  */
 extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...);
 
-extern char *__d_path(const struct path *path, struct path *root, char *, int);
+extern int __d_path(const struct path *path, struct path *root, char **, int *);
 extern char *d_path(const struct path *, char *, int);
 extern char *dentry_path(struct dentry *, char *, int);
 
Index: linux-2.6/security/tomoyo/realpath.c
===================================================================
--- linux-2.6.orig/security/tomoyo/realpath.c	2010-05-27 12:13:46.000000000 +0200
+++ linux-2.6/security/tomoyo/realpath.c	2010-05-27 12:13:48.000000000 +0200
@@ -77,7 +77,7 @@ int tomoyo_encode(char *buffer, int bufl
 int tomoyo_realpath_from_path2(struct path *path, char *newname,
 			       int newname_len)
 {
-	int error = -ENOMEM;
+	int error = 0;
 	struct dentry *dentry = path->dentry;
 	char *sp;
 
@@ -88,26 +88,29 @@ int tomoyo_realpath_from_path2(struct pa
 		static const int offset = 1536;
 		sp = dentry->d_op->d_dname(dentry, newname + offset,
 					   newname_len - offset);
+		if (IS_ERR(sp))
+			error = PTR_ERR(sp);
 	} else {
 		struct path ns_root = {.mnt = NULL, .dentry = NULL};
+		int buflen = newname_len - 1;
 
+		sp = newname + buflen;
+		*sp = '\0';
 		spin_lock(&dcache_lock);
 		/* go to whatever namespace root we are under */
-		sp = __d_path(path, &ns_root, newname, newname_len);
+		error = __d_path(path, &ns_root, &sp, &buflen);
 		spin_unlock(&dcache_lock);
 		/* Prepend "/proc" prefix if using internal proc vfs mount. */
-		if (!IS_ERR(sp) && (path->mnt->mnt_flags & MNT_INTERNAL) &&
+		if (!error && (path->mnt->mnt_flags & MNT_INTERNAL) &&
 		    (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) {
 			sp -= 5;
 			if (sp >= newname)
 				memcpy(sp, "/proc", 5);
 			else
-				sp = ERR_PTR(-ENOMEM);
+				error = -ENOMEM;
 		}
 	}
-	if (IS_ERR(sp))
-		error = PTR_ERR(sp);
-	else
+	if (!error)
 		error = tomoyo_encode(newname, sp - newname, sp);
 	/* Append trailing '/' if dentry is a directory. */
 	if (!error && dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists