lkml.org 
[lkml]   [2019]   [Sep]   [11]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH] fat: Workaround the race with userspace's read via blockdev while mounting
    Date
    If userspace reads the buffer via blockdev while mounting,
    sb_getblk()+modify can race with buffer read via blockdev.

    For example,

    FS userspace
    bh = sb_getblk()
    modify bh->b_data
    read
    ll_rw_block(bh)
    fill bh->b_data by on-disk data
    /* lost modified data by FS */
    set_buffer_uptodate(bh)
    set_buffer_uptodate(bh)

    The userspace should not use the blockdev while mounting though, the
    udev seems to be already doing this. Although I think the udev should
    try to avoid this, workaround the race by small overhead.

    Reported-by: Jan Stancek <jstancek@redhat.com>
    Tested-by: Jan Stancek <jstancek@redhat.com>
    Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
    ---

    fs/fat/dir.c | 13 +++++++++++--
    fs/fat/fatent.c | 3 +++
    2 files changed, 14 insertions(+), 2 deletions(-)

    diff -puN fs/fat/dir.c~fat-workaround-getblk fs/fat/dir.c
    --- linux/fs/fat/dir.c~fat-workaround-getblk 2019-09-10 09:29:51.137292020 +0900
    +++ linux-hirofumi/fs/fat/dir.c 2019-09-10 09:39:15.366295152 +0900
    @@ -1100,8 +1100,11 @@ static int fat_zeroed_cluster(struct ino
    err = -ENOMEM;
    goto error;
    }
    + /* Avoid race with userspace read via bdev */
    + lock_buffer(bhs[n]);
    memset(bhs[n]->b_data, 0, sb->s_blocksize);
    set_buffer_uptodate(bhs[n]);
    + unlock_buffer(bhs[n]);
    mark_buffer_dirty_inode(bhs[n], dir);

    n++;
    @@ -1158,6 +1161,8 @@ int fat_alloc_new_dir(struct inode *dir,
    fat_time_unix2fat(sbi, ts, &time, &date, &time_cs);

    de = (struct msdos_dir_entry *)bhs[0]->b_data;
    + /* Avoid race with userspace read via bdev */
    + lock_buffer(bhs[0]);
    /* filling the new directory slots ("." and ".." entries) */
    memcpy(de[0].name, MSDOS_DOT, MSDOS_NAME);
    memcpy(de[1].name, MSDOS_DOTDOT, MSDOS_NAME);
    @@ -1180,6 +1185,7 @@ int fat_alloc_new_dir(struct inode *dir,
    de[0].size = de[1].size = 0;
    memset(de + 2, 0, sb->s_blocksize - 2 * sizeof(*de));
    set_buffer_uptodate(bhs[0]);
    + unlock_buffer(bhs[0]);
    mark_buffer_dirty_inode(bhs[0], dir);

    err = fat_zeroed_cluster(dir, blknr, 1, bhs, MAX_BUF_PER_PAGE);
    @@ -1237,11 +1243,14 @@ static int fat_add_new_entries(struct in

    /* fill the directory entry */
    copy = min(size, sb->s_blocksize);
    + /* Avoid race with userspace read via bdev */
    + lock_buffer(bhs[n]);
    memcpy(bhs[n]->b_data, slots, copy);
    - slots += copy;
    - size -= copy;
    set_buffer_uptodate(bhs[n]);
    + unlock_buffer(bhs[n]);
    mark_buffer_dirty_inode(bhs[n], dir);
    + slots += copy;
    + size -= copy;
    if (!size)
    break;
    n++;
    diff -puN fs/fat/fatent.c~fat-workaround-getblk fs/fat/fatent.c
    --- linux/fs/fat/fatent.c~fat-workaround-getblk 2019-09-10 09:36:20.247225406 +0900
    +++ linux-hirofumi/fs/fat/fatent.c 2019-09-10 09:36:43.847100048 +0900
    @@ -388,8 +388,11 @@ static int fat_mirror_bhs(struct super_b
    err = -ENOMEM;
    goto error;
    }
    + /* Avoid race with userspace read via bdev */
    + lock_buffer(c_bh);
    memcpy(c_bh->b_data, bhs[n]->b_data, sb->s_blocksize);
    set_buffer_uptodate(c_bh);
    + unlock_buffer(c_bh);
    mark_buffer_dirty_inode(c_bh, sbi->fat_inode);
    if (sb->s_flags & SB_SYNCHRONOUS)
    err = sync_dirty_buffer(c_bh);
    _
    \
     
     \ /
      Last update: 2019-09-11 08:57    [W:2.449 / U:0.008 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site