Commit 3172f8fe authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  fix apparmor dereferencing potentially freed dentry, sanitize __d_path() API
parents b835c0f4 02125a82
...@@ -2439,16 +2439,14 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name) ...@@ -2439,16 +2439,14 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name)
/** /**
* prepend_path - Prepend path string to a buffer * prepend_path - Prepend path string to a buffer
* @path: the dentry/vfsmount to report * @path: the dentry/vfsmount to report
* @root: root vfsmnt/dentry (may be modified by this function) * @root: root vfsmnt/dentry
* @buffer: pointer to the end of the buffer * @buffer: pointer to the end of the buffer
* @buflen: pointer to buffer length * @buflen: pointer to buffer length
* *
* Caller holds the rename_lock. * Caller holds the rename_lock.
*
* If path is not reachable from the supplied root, then the value of
* root is changed (without modifying refcounts).
*/ */
static int prepend_path(const struct path *path, struct path *root, static int prepend_path(const struct path *path,
const struct path *root,
char **buffer, int *buflen) char **buffer, int *buflen)
{ {
struct dentry *dentry = path->dentry; struct dentry *dentry = path->dentry;
...@@ -2483,10 +2481,10 @@ static int prepend_path(const struct path *path, struct path *root, ...@@ -2483,10 +2481,10 @@ static int prepend_path(const struct path *path, struct path *root,
dentry = parent; dentry = parent;
} }
out:
if (!error && !slash) if (!error && !slash)
error = prepend(buffer, buflen, "/", 1); error = prepend(buffer, buflen, "/", 1);
out:
br_read_unlock(vfsmount_lock); br_read_unlock(vfsmount_lock);
return error; return error;
...@@ -2500,15 +2498,17 @@ static int prepend_path(const struct path *path, struct path *root, ...@@ -2500,15 +2498,17 @@ static int prepend_path(const struct path *path, struct path *root,
WARN(1, "Root dentry has weird name <%.*s>\n", WARN(1, "Root dentry has weird name <%.*s>\n",
(int) dentry->d_name.len, dentry->d_name.name); (int) dentry->d_name.len, dentry->d_name.name);
} }
root->mnt = vfsmnt; if (!slash)
root->dentry = dentry; error = prepend(buffer, buflen, "/", 1);
if (!error)
error = vfsmnt->mnt_ns ? 1 : 2;
goto out; goto out;
} }
/** /**
* __d_path - return the path of a dentry * __d_path - return the path of a dentry
* @path: the dentry/vfsmount to report * @path: the dentry/vfsmount to report
* @root: root vfsmnt/dentry (may be modified by this function) * @root: root vfsmnt/dentry
* @buf: buffer to return value in * @buf: buffer to return value in
* @buflen: buffer length * @buflen: buffer length
* *
...@@ -2519,10 +2519,10 @@ static int prepend_path(const struct path *path, struct path *root, ...@@ -2519,10 +2519,10 @@ static int prepend_path(const struct path *path, struct path *root,
* *
* "buflen" should be positive. * "buflen" should be positive.
* *
* If path is not reachable from the supplied root, then the value of * If the path is not reachable from the supplied root, return %NULL.
* root is changed (without modifying refcounts).
*/ */
char *__d_path(const struct path *path, struct path *root, char *__d_path(const struct path *path,
const struct path *root,
char *buf, int buflen) char *buf, int buflen)
{ {
char *res = buf + buflen; char *res = buf + buflen;
...@@ -2533,7 +2533,28 @@ char *__d_path(const struct path *path, struct path *root, ...@@ -2533,7 +2533,28 @@ char *__d_path(const struct path *path, struct path *root,
error = prepend_path(path, root, &res, &buflen); error = prepend_path(path, root, &res, &buflen);
write_sequnlock(&rename_lock); write_sequnlock(&rename_lock);
if (error) if (error < 0)
return ERR_PTR(error);
if (error > 0)
return NULL;
return res;
}
char *d_absolute_path(const struct path *path,
char *buf, int buflen)
{
struct path root = {};
char *res = buf + buflen;
int error;
prepend(&res, &buflen, "\0", 1);
write_seqlock(&rename_lock);
error = prepend_path(path, &root, &res, &buflen);
write_sequnlock(&rename_lock);
if (error > 1)
error = -EINVAL;
if (error < 0)
return ERR_PTR(error); return ERR_PTR(error);
return res; return res;
} }
...@@ -2541,7 +2562,8 @@ char *__d_path(const struct path *path, struct path *root, ...@@ -2541,7 +2562,8 @@ char *__d_path(const struct path *path, struct path *root,
/* /*
* same as __d_path but appends "(deleted)" for unlinked files. * same as __d_path but appends "(deleted)" for unlinked files.
*/ */
static int path_with_deleted(const struct path *path, struct path *root, static int path_with_deleted(const struct path *path,
const struct path *root,
char **buf, int *buflen) char **buf, int *buflen)
{ {
prepend(buf, buflen, "\0", 1); prepend(buf, buflen, "\0", 1);
...@@ -2579,7 +2601,6 @@ char *d_path(const struct path *path, char *buf, int buflen) ...@@ -2579,7 +2601,6 @@ char *d_path(const struct path *path, char *buf, int buflen)
{ {
char *res = buf + buflen; char *res = buf + buflen;
struct path root; struct path root;
struct path tmp;
int error; int error;
/* /*
...@@ -2594,9 +2615,8 @@ char *d_path(const struct path *path, char *buf, int buflen) ...@@ -2594,9 +2615,8 @@ char *d_path(const struct path *path, char *buf, int buflen)
get_fs_root(current->fs, &root); get_fs_root(current->fs, &root);
write_seqlock(&rename_lock); write_seqlock(&rename_lock);
tmp = root; error = path_with_deleted(path, &root, &res, &buflen);
error = path_with_deleted(path, &tmp, &res, &buflen); if (error < 0)
if (error)
res = ERR_PTR(error); res = ERR_PTR(error);
write_sequnlock(&rename_lock); write_sequnlock(&rename_lock);
path_put(&root); path_put(&root);
...@@ -2617,7 +2637,6 @@ char *d_path_with_unreachable(const struct path *path, char *buf, int buflen) ...@@ -2617,7 +2637,6 @@ char *d_path_with_unreachable(const struct path *path, char *buf, int buflen)
{ {
char *res = buf + buflen; char *res = buf + buflen;
struct path root; struct path root;
struct path tmp;
int error; int error;
if (path->dentry->d_op && path->dentry->d_op->d_dname) if (path->dentry->d_op && path->dentry->d_op->d_dname)
...@@ -2625,9 +2644,8 @@ char *d_path_with_unreachable(const struct path *path, char *buf, int buflen) ...@@ -2625,9 +2644,8 @@ char *d_path_with_unreachable(const struct path *path, char *buf, int buflen)
get_fs_root(current->fs, &root); get_fs_root(current->fs, &root);
write_seqlock(&rename_lock); write_seqlock(&rename_lock);
tmp = root; error = path_with_deleted(path, &root, &res, &buflen);
error = path_with_deleted(path, &tmp, &res, &buflen); if (error > 0)
if (!error && !path_equal(&tmp, &root))
error = prepend_unreachable(&res, &buflen); error = prepend_unreachable(&res, &buflen);
write_sequnlock(&rename_lock); write_sequnlock(&rename_lock);
path_put(&root); path_put(&root);
...@@ -2758,19 +2776,18 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size) ...@@ -2758,19 +2776,18 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
write_seqlock(&rename_lock); write_seqlock(&rename_lock);
if (!d_unlinked(pwd.dentry)) { if (!d_unlinked(pwd.dentry)) {
unsigned long len; unsigned long len;
struct path tmp = root;
char *cwd = page + PAGE_SIZE; char *cwd = page + PAGE_SIZE;
int buflen = PAGE_SIZE; int buflen = PAGE_SIZE;
prepend(&cwd, &buflen, "\0", 1); prepend(&cwd, &buflen, "\0", 1);
error = prepend_path(&pwd, &tmp, &cwd, &buflen); error = prepend_path(&pwd, &root, &cwd, &buflen);
write_sequnlock(&rename_lock); write_sequnlock(&rename_lock);
if (error) if (error < 0)
goto out; goto out;
/* Unreachable from current root */ /* Unreachable from current root */
if (!path_equal(&tmp, &root)) { if (error > 0) {
error = prepend_unreachable(&cwd, &buflen); error = prepend_unreachable(&cwd, &buflen);
if (error) if (error)
goto out; goto out;
......
...@@ -1048,15 +1048,12 @@ static int show_mountinfo(struct seq_file *m, void *v) ...@@ -1048,15 +1048,12 @@ static int show_mountinfo(struct seq_file *m, void *v)
if (err) if (err)
goto out; goto out;
seq_putc(m, ' '); seq_putc(m, ' ');
seq_path_root(m, &mnt_path, &root, " \t\n\\");
if (root.mnt != p->root.mnt || root.dentry != p->root.dentry) { /* mountpoints outside of chroot jail will give SEQ_SKIP on this */
/* err = seq_path_root(m, &mnt_path, &root, " \t\n\\");
* Mountpoint is outside root, discard that one. Ugly, if (err)
* but less so than trying to do that in iterator in a goto out;
* race-free way (due to renames).
*/
return SEQ_SKIP;
}
seq_puts(m, mnt->mnt_flags & MNT_READONLY ? " ro" : " rw"); seq_puts(m, mnt->mnt_flags & MNT_READONLY ? " ro" : " rw");
show_mnt_opts(m, mnt); show_mnt_opts(m, mnt);
...@@ -2776,3 +2773,8 @@ void kern_unmount(struct vfsmount *mnt) ...@@ -2776,3 +2773,8 @@ void kern_unmount(struct vfsmount *mnt)
} }
} }
EXPORT_SYMBOL(kern_unmount); EXPORT_SYMBOL(kern_unmount);
bool our_mnt(struct vfsmount *mnt)
{
return check_mnt(mnt);
}
...@@ -449,8 +449,6 @@ EXPORT_SYMBOL(seq_path); ...@@ -449,8 +449,6 @@ EXPORT_SYMBOL(seq_path);
/* /*
* Same as seq_path, but relative to supplied root. * Same as seq_path, but relative to supplied root.
*
* root may be changed, see __d_path().
*/ */
int seq_path_root(struct seq_file *m, struct path *path, struct path *root, int seq_path_root(struct seq_file *m, struct path *path, struct path *root,
char *esc) char *esc)
...@@ -463,6 +461,8 @@ int seq_path_root(struct seq_file *m, struct path *path, struct path *root, ...@@ -463,6 +461,8 @@ int seq_path_root(struct seq_file *m, struct path *path, struct path *root,
char *p; char *p;
p = __d_path(path, root, buf, size); p = __d_path(path, root, buf, size);
if (!p)
return SEQ_SKIP;
res = PTR_ERR(p); res = PTR_ERR(p);
if (!IS_ERR(p)) { if (!IS_ERR(p)) {
char *end = mangle_path(buf, p, esc); char *end = mangle_path(buf, p, esc);
...@@ -474,7 +474,7 @@ int seq_path_root(struct seq_file *m, struct path *path, struct path *root, ...@@ -474,7 +474,7 @@ int seq_path_root(struct seq_file *m, struct path *path, struct path *root,
} }
seq_commit(m, res); seq_commit(m, res);
return res < 0 ? res : 0; return res < 0 && res != -ENAMETOOLONG ? res : 0;
} }
/* /*
......
...@@ -339,7 +339,8 @@ extern int d_validate(struct dentry *, struct dentry *); ...@@ -339,7 +339,8 @@ extern int d_validate(struct dentry *, struct dentry *);
*/ */
extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...); extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...);
extern char *__d_path(const struct path *path, struct path *root, char *, int); extern char *__d_path(const struct path *, const struct path *, char *, int);
extern char *d_absolute_path(const struct path *, char *, int);
extern char *d_path(const struct path *, char *, int); extern char *d_path(const struct path *, char *, int);
extern char *d_path_with_unreachable(const struct path *, char *, int); extern char *d_path_with_unreachable(const struct path *, char *, int);
extern char *dentry_path_raw(struct dentry *, char *, int); extern char *dentry_path_raw(struct dentry *, char *, int);
......
...@@ -1942,6 +1942,7 @@ extern int fd_statfs(int, struct kstatfs *); ...@@ -1942,6 +1942,7 @@ extern int fd_statfs(int, struct kstatfs *);
extern int statfs_by_dentry(struct dentry *, struct kstatfs *); extern int statfs_by_dentry(struct dentry *, struct kstatfs *);
extern int freeze_super(struct super_block *super); extern int freeze_super(struct super_block *super);
extern int thaw_super(struct super_block *super); extern int thaw_super(struct super_block *super);
extern bool our_mnt(struct vfsmount *mnt);
extern int current_umask(void); extern int current_umask(void);
......
...@@ -57,23 +57,44 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen) ...@@ -57,23 +57,44 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen)
static int d_namespace_path(struct path *path, char *buf, int buflen, static int d_namespace_path(struct path *path, char *buf, int buflen,
char **name, int flags) char **name, int flags)
{ {
struct path root, tmp;
char *res; char *res;
int connected, error = 0; int error = 0;
int connected = 1;
/* Get the root we want to resolve too, released below */ if (path->mnt->mnt_flags & MNT_INTERNAL) {
/* it's not mounted anywhere */
res = dentry_path(path->dentry, buf, buflen);
*name = res;
if (IS_ERR(res)) {
*name = buf;
return PTR_ERR(res);
}
if (path->dentry->d_sb->s_magic == PROC_SUPER_MAGIC &&
strncmp(*name, "/sys/", 5) == 0) {
/* TODO: convert over to using a per namespace
* control instead of hard coded /proc
*/
return prepend(name, *name - buf, "/proc", 5);
}
return 0;
}
/* resolve paths relative to chroot?*/
if (flags & PATH_CHROOT_REL) { if (flags & PATH_CHROOT_REL) {
/* resolve paths relative to chroot */ struct path root;
get_fs_root(current->fs, &root); get_fs_root(current->fs, &root);
} else { res = __d_path(path, &root, buf, buflen);
/* resolve paths relative to namespace */ if (res && !IS_ERR(res)) {
root.mnt = current->nsproxy->mnt_ns->root; /* everything's fine */
root.dentry = root.mnt->mnt_root; *name = res;
path_get(&root); path_put(&root);
goto ok;
}
path_put(&root);
connected = 0;
} }
tmp = root; res = d_absolute_path(path, buf, buflen);
res = __d_path(path, &tmp, buf, buflen);
*name = res; *name = res;
/* handle error conditions - and still allow a partial path to /* handle error conditions - and still allow a partial path to
...@@ -84,7 +105,10 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, ...@@ -84,7 +105,10 @@ static int d_namespace_path(struct path *path, char *buf, int buflen,
*name = buf; *name = buf;
goto out; goto out;
} }
if (!our_mnt(path->mnt))
connected = 0;
ok:
/* Handle two cases: /* Handle two cases:
* 1. A deleted dentry && profile is not allowing mediation of deleted * 1. A deleted dentry && profile is not allowing mediation of deleted
* 2. On some filesystems, newly allocated dentries appear to the * 2. On some filesystems, newly allocated dentries appear to the
...@@ -97,10 +121,7 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, ...@@ -97,10 +121,7 @@ static int d_namespace_path(struct path *path, char *buf, int buflen,
goto out; goto out;
} }
/* Determine if the path is connected to the expected root */ /* If the path is not connected to the expected root,
connected = tmp.dentry == root.dentry && tmp.mnt == root.mnt;
/* If the path is not connected,
* check if it is a sysctl and handle specially else remove any * check if it is a sysctl and handle specially else remove any
* leading / that __d_path may have returned. * leading / that __d_path may have returned.
* Unless * Unless
...@@ -112,17 +133,9 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, ...@@ -112,17 +133,9 @@ static int d_namespace_path(struct path *path, char *buf, int buflen,
* namespace root. * namespace root.
*/ */
if (!connected) { if (!connected) {
/* is the disconnect path a sysctl? */ if (!(flags & PATH_CONNECT_PATH) &&
if (tmp.dentry->d_sb->s_magic == PROC_SUPER_MAGIC &&
strncmp(*name, "/sys/", 5) == 0) {
/* TODO: convert over to using a per namespace
* control instead of hard coded /proc
*/
error = prepend(name, *name - buf, "/proc", 5);
} else if (!(flags & PATH_CONNECT_PATH) &&
!(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) &&
(tmp.mnt == current->nsproxy->mnt_ns->root && our_mnt(path->mnt))) {
tmp.dentry == tmp.mnt->mnt_root))) {
/* disconnected path, don't return pathname starting /* disconnected path, don't return pathname starting
* with '/' * with '/'
*/ */
...@@ -133,8 +146,6 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, ...@@ -133,8 +146,6 @@ static int d_namespace_path(struct path *path, char *buf, int buflen,
} }
out: out:
path_put(&root);
return error; return error;
} }
......
...@@ -101,9 +101,8 @@ static char *tomoyo_get_absolute_path(struct path *path, char * const buffer, ...@@ -101,9 +101,8 @@ static char *tomoyo_get_absolute_path(struct path *path, char * const buffer,
{ {
char *pos = ERR_PTR(-ENOMEM); char *pos = ERR_PTR(-ENOMEM);
if (buflen >= 256) { if (buflen >= 256) {
struct path ns_root = { };
/* go to whatever namespace root we are under */ /* go to whatever namespace root we are under */
pos = __d_path(path, &ns_root, buffer, buflen - 1); pos = d_absolute_path(path, buffer, buflen - 1);
if (!IS_ERR(pos) && *pos == '/' && pos[1]) { if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
struct inode *inode = path->dentry->d_inode; struct inode *inode = path->dentry->d_inode;
if (inode && S_ISDIR(inode->i_mode)) { if (inode && S_ISDIR(inode->i_mode)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment