lkml.org 
[lkml]   [2015]   [May]   [5]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 79/79] namei: stretch RCU mode into get_link()
    Date
    From: Al Viro <viro@zeniv.linux.org.uk>

    one potentially subtle point: may_follow_link() kludge rejecting symlink
    traversal in RCU mode is handled by returning -ECHILD and repeating
    everything in non-RCU mode. Reason: audit_log_link_denied() won't work
    in RCU mode.

    Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
    ---
    fs/namei.c | 86 ++++++++++++++++++++++++++++++++++----------------------------
    1 file changed, 47 insertions(+), 39 deletions(-)

    diff --git a/fs/namei.c b/fs/namei.c
    index cf8f2de..761d4b1 100644
    --- a/fs/namei.c
    +++ b/fs/namei.c
    @@ -725,6 +725,29 @@ static int complete_walk(struct nameidata *nd)
    return status;
    }

    +static inline void put_link(struct nameidata *nd)
    +{
    + struct saved *last = nd->stack + --nd->depth;
    + struct inode *inode = last->link.dentry->d_inode;
    + if (last->cookie && inode->i_op->put_link)
    + inode->i_op->put_link(last->link.dentry, last->cookie);
    + path_put(&last->link);
    +}
    +
    +static void terminate_walk(struct nameidata *nd)
    +{
    + if (!(nd->flags & LOOKUP_RCU)) {
    + path_put(&nd->path);
    + } else {
    + nd->flags &= ~LOOKUP_RCU;
    + if (!(nd->flags & LOOKUP_ROOT))
    + nd->root.mnt = NULL;
    + rcu_read_unlock();
    + }
    + while (unlikely(nd->depth))
    + put_link(nd);
    +}
    +
    static __always_inline void set_root(struct nameidata *nd)
    {
    get_fs_root(current->fs, &nd->root);
    @@ -776,15 +799,6 @@ void nd_jump_link(struct path *path)
    nd->flags |= LOOKUP_JUMPED;
    }

    -static inline void put_link(struct nameidata *nd)
    -{
    - struct saved *last = nd->stack + --nd->depth;
    - struct inode *inode = last->link.dentry->d_inode;
    - if (last->cookie && inode->i_op->put_link)
    - inode->i_op->put_link(last->link.dentry, last->cookie);
    - path_put(&last->link);
    -}
    -
    int sysctl_protected_symlinks __read_mostly = 0;
    int sysctl_protected_hardlinks __read_mostly = 0;

    @@ -804,21 +818,20 @@ int sysctl_protected_hardlinks __read_mostly = 0;
    *
    * Returns 0 if following the symlink is allowed, -ve on error.
    */
    -static inline int may_follow_link(struct path *link, struct nameidata *nd)
    +static inline int may_follow_link(struct path *link, const struct inode *inode,
    + struct nameidata *nd)
    {
    - const struct inode *inode;
    const struct inode *parent;

    if (!sysctl_protected_symlinks)
    return 0;

    /* Allowed if owner and follower match. */
    - inode = link->dentry->d_inode;
    if (uid_eq(current_cred()->fsuid, inode->i_uid))
    return 0;

    /* Allowed if parent directory not sticky and world-writable. */
    - parent = nd->path.dentry->d_inode;
    + parent = nd->inode;
    if ((parent->i_mode & (S_ISVTX|S_IWOTH)) != (S_ISVTX|S_IWOTH))
    return 0;

    @@ -826,9 +839,14 @@ static inline int may_follow_link(struct path *link, struct nameidata *nd)
    if (uid_eq(parent->i_uid, inode->i_uid))
    return 0;

    + if (nd->flags & LOOKUP_RCU) {
    + terminate_walk(nd);
    + return -ECHILD;
    + }
    +
    audit_log_link_denied("follow_link", link);
    - path_put_conditional(link, nd);
    - path_put(&nd->path);
    + path_to_nameidata(link, nd);
    + terminate_walk(nd);
    return -EACCES;
    }

    @@ -902,15 +920,19 @@ static int may_linkat(struct path *link)
    }

    static __always_inline
    -const char *get_link(struct nameidata *nd)
    +const char *get_link(struct nameidata *nd, struct inode *inode)
    {
    struct saved *last = nd->stack + nd->depth;
    struct dentry *dentry = nd->link.dentry;
    - struct inode *inode = dentry->d_inode;
    int error;
    const char *res;

    - BUG_ON(nd->flags & LOOKUP_RCU);
    + if (nd->flags & LOOKUP_RCU) {
    + if (unlikely(nd->link.mnt != nd->path.mnt ||
    + unlazy_walk(nd, dentry))) {
    + return ERR_PTR(-ECHILD);
    + }
    + }

    if (nd->link.mnt == nd->path.mnt)
    mntget(nd->link.mnt);
    @@ -1553,20 +1575,6 @@ static inline int handle_dots(struct nameidata *nd, int type)
    return 0;
    }

    -static void terminate_walk(struct nameidata *nd)
    -{
    - if (!(nd->flags & LOOKUP_RCU)) {
    - path_put(&nd->path);
    - } else {
    - nd->flags &= ~LOOKUP_RCU;
    - if (!(nd->flags & LOOKUP_ROOT))
    - nd->root.mnt = NULL;
    - rcu_read_unlock();
    - }
    - while (unlikely(nd->depth))
    - put_link(nd);
    -}
    -
    static int pick_link(struct nameidata *nd, struct path *link)
    {
    int error;
    @@ -1847,16 +1855,16 @@ OK:

    if (err) {
    const char *s;
    + struct inode *inode = nd->link.dentry->d_inode;

    if (nd->flags & LOOKUP_RCU) {
    - if (unlikely(nd->link.mnt != nd->path.mnt ||
    - unlazy_walk(nd, nd->link.dentry))) {
    + if (unlikely(is_stale(nd, nd->link.dentry))) {
    err = -ECHILD;
    break;
    }
    }

    - s = get_link(nd);
    + s = get_link(nd, inode);

    if (unlikely(IS_ERR(s))) {
    err = PTR_ERR(s);
    @@ -2009,20 +2017,20 @@ static void path_cleanup(struct nameidata *nd)
    static int trailing_symlink(struct nameidata *nd)
    {
    const char *s;
    + struct inode *inode = nd->link.dentry->d_inode;
    int error;

    if (nd->flags & LOOKUP_RCU) {
    - if (unlikely(nd->link.mnt != nd->path.mnt ||
    - unlazy_walk(nd, nd->link.dentry))) {
    + if (unlikely(is_stale(nd, nd->link.dentry))) {
    terminate_walk(nd);
    return -ECHILD;
    }
    }
    - error = may_follow_link(&nd->link, nd);
    + error = may_follow_link(&nd->link, inode, nd);
    if (unlikely(error))
    return error;
    nd->flags |= LOOKUP_PARENT;
    - s = get_link(nd);
    + s = get_link(nd, inode);
    if (unlikely(IS_ERR(s))) {
    terminate_walk(nd);
    return PTR_ERR(s);
    --
    2.1.4


    \
     
     \ /
      Last update: 2015-05-05 07:41    [W:4.528 / U:0.048 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site