forked from Minki/linux
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: Aleksa Sarai <cyphar@cyphar.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
1bc82070fa
commit
740a167827
35
fs/namei.c
35
fs/namei.c
@ -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)))
|
||||
return s;
|
||||
return ERR_PTR(-ECHILD);
|
||||
error = nd_jump_root(nd);
|
||||
if (unlikely(error))
|
||||
return ERR_PTR(error);
|
||||
return s;
|
||||
} else if (nd->dfd == AT_FDCWD) {
|
||||
if (flags & LOOKUP_RCU) {
|
||||
struct fs_struct *fs = current->fs;
|
||||
|
Loading…
Reference in New Issue
Block a user