Commit 740a1678 authored by Aleksa Sarai's avatar Aleksa Sarai Committed by Al Viro
Browse files

namei: allow set_root() to produce errors



For LOOKUP_BENEATH and LOOKUP_IN_ROOT it is necessary to ensure that
set_root() is never called, and thus (for hardening purposes) it should
return an error rather than permit a breakout from the root. In
addition, move all of the repetitive set_root() calls to nd_jump_root().

Signed-off-by: default avatarAleksa Sarai <cyphar@cyphar.com>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 1bc82070
Loading
Loading
Loading
Loading
+24 −11
Original line number Diff line number Diff line
@@ -798,7 +798,7 @@ static int complete_walk(struct nameidata *nd)
	return status;
}

static void set_root(struct nameidata *nd)
static int set_root(struct nameidata *nd)
{
	struct fs_struct *fs = current->fs;

@@ -814,6 +814,7 @@ static void set_root(struct nameidata *nd)
		get_fs_root(fs, &nd->root);
		nd->flags |= LOOKUP_ROOT_GRABBED;
	}
	return 0;
}

static void path_put_conditional(struct path *path, struct nameidata *nd)
@@ -837,6 +838,11 @@ static inline void path_to_nameidata(const struct path *path,

static int nd_jump_root(struct nameidata *nd)
{
	if (!nd->root.mnt) {
		int error = set_root(nd);
		if (error)
			return error;
	}
	if (nd->flags & LOOKUP_RCU) {
		struct dentry *d;
		nd->path = nd->root;
@@ -1084,10 +1090,9 @@ const char *get_link(struct nameidata *nd)
			return res;
	}
	if (*res == '/') {
		if (!nd->root.mnt)
			set_root(nd);
		if (unlikely(nd_jump_root(nd)))
			return ERR_PTR(-ECHILD);
		error = nd_jump_root(nd);
		if (unlikely(error))
			return ERR_PTR(error);
		while (unlikely(*++res == '/'))
			;
	}
@@ -1700,8 +1705,13 @@ static inline int may_lookup(struct nameidata *nd)
static inline int handle_dots(struct nameidata *nd, int type)
{
	if (type == LAST_DOTDOT) {
		if (!nd->root.mnt)
			set_root(nd);
		int error = 0;

		if (!nd->root.mnt) {
			error = set_root(nd);
			if (error)
				return error;
		}
		if (nd->flags & LOOKUP_RCU) {
			return follow_dotdot_rcu(nd);
		} else
@@ -2159,6 +2169,7 @@ OK:
/* must be paired with terminate_walk() */
static const char *path_init(struct nameidata *nd, unsigned flags)
{
	int error;
	const char *s = nd->name->name;

	if (!*s)
@@ -2191,11 +2202,13 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
	nd->path.dentry = NULL;

	nd->m_seq = read_seqbegin(&mount_lock);

	/* Figure out the starting path and root (if needed). */
	if (*s == '/') {
		set_root(nd);
		if (likely(!nd_jump_root(nd)))
		error = nd_jump_root(nd);
		if (unlikely(error))
			return ERR_PTR(error);
		return s;
		return ERR_PTR(-ECHILD);
	} else if (nd->dfd == AT_FDCWD) {
		if (flags & LOOKUP_RCU) {
			struct fs_struct *fs = current->fs;