lkml.org 
[lkml]   [2013]   [May]   [7]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 070/118] sysfs: fix use after free in case of concurrent read/write and readdir
    Date
    3.5.7.12 -stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Ming Lei <ming.lei@canonical.com>

    commit f7db5e7660b122142410dcf36ba903c73d473250 upstream.

    The inode->i_mutex isn't hold when updating filp->f_pos
    in read()/write(), so the filp->f_pos might be read as
    0 or 1 in readdir() when there is concurrent read()/write()
    on this same file, then may cause use after free in readdir().

    The bug can be reproduced with Li Zefan's test code on the
    link:

    https://patchwork.kernel.org/patch/2160771/

    This patch fixes the use after free under this situation.

    Reported-by: Li Zefan <lizefan@huawei.com>
    Signed-off-by: Ming Lei <ming.lei@canonical.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    Signed-off-by: Luis Henriques <luis.henriques@canonical.com>
    ---
    fs/sysfs/dir.c | 15 +++++++++++----
    1 file changed, 11 insertions(+), 4 deletions(-)

    diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
    index 8777436..1a32e0f 100644
    --- a/fs/sysfs/dir.c
    +++ b/fs/sysfs/dir.c
    @@ -1013,6 +1013,7 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
    enum kobj_ns_type type;
    const void *ns;
    ino_t ino;
    + loff_t off;

    type = sysfs_ns_type(parent_sd);
    ns = sysfs_info(dentry->d_sb)->ns[type];
    @@ -1035,6 +1036,7 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
    return 0;
    }
    mutex_lock(&sysfs_mutex);
    + off = filp->f_pos;
    for (pos = sysfs_dir_pos(ns, parent_sd, filp->f_pos, pos);
    pos;
    pos = sysfs_dir_next_pos(ns, parent_sd, filp->f_pos, pos)) {
    @@ -1046,19 +1048,24 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
    len = strlen(name);
    ino = pos->s_ino;
    type = dt_type(pos);
    - filp->f_pos = pos->s_hash;
    + off = filp->f_pos = pos->s_hash;
    filp->private_data = sysfs_get(pos);

    mutex_unlock(&sysfs_mutex);
    - ret = filldir(dirent, name, len, filp->f_pos, ino, type);
    + ret = filldir(dirent, name, len, off, ino, type);
    mutex_lock(&sysfs_mutex);
    if (ret < 0)
    break;
    }
    mutex_unlock(&sysfs_mutex);
    - if ((filp->f_pos > 1) && !pos) { /* EOF */
    - filp->f_pos = INT_MAX;
    +
    + /* don't reference last entry if its refcount is dropped */
    + if (!pos) {
    filp->private_data = NULL;
    +
    + /* EOF and not changed as 0 or 1 in read/write path */
    + if (off == filp->f_pos && off > 1)
    + filp->f_pos = INT_MAX;
    }
    return 0;
    }
    --
    1.8.1.2


    \
     
     \ /
      Last update: 2013-05-07 16:44    [W:4.203 / U:0.012 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site