• V
    fuse: fix write deadlock · 258c2b71
    Vivek Goyal 提交于
    stable inclusion
    from stable-5.10.36
    commit 1c525c265668176301bac4f152dd49a3c51c7ac6
    bugzilla: 51867
    CVE: NA
    
    --------------------------------
    
    commit 4f06dd92 upstream.
    
    There are two modes for write(2) and friends in fuse:
    
    a) write through (update page cache, send sync WRITE request to userspace)
    
    b) buffered write (update page cache, async writeout later)
    
    The write through method kept all the page cache pages locked that were
    used for the request.  Keeping more than one page locked is deadlock prone
    and Qian Cai demonstrated this with trinity fuzzing.
    
    The reason for keeping the pages locked is that concurrent mapped reads
    shouldn't try to pull possibly stale data into the page cache.
    
    For full page writes, the easy way to fix this is to make the cached page
    be the authoritative source by marking the page PG_uptodate immediately.
    After this the page can be safely unlocked, since mapped/cached reads will
    take the written data from the cache.
    
    Concurrent mapped writes will now cause data in the original WRITE request
    to be updated; this however doesn't cause any data inconsistency and this
    scenario should be exceedingly rare anyway.
    
    If the WRITE request returns with an error in the above case, currently the
    page is not marked uptodate; this means that a concurrent read will always
    read consistent data.  After this patch the page is uptodate between
    writing to the cache and receiving the error: there's window where a cached
    read will read the wrong data.  While theoretically this could be a
    regression, it is unlikely to be one in practice, since this is normal for
    buffered writes.
    
    In case of a partial page write to an already uptodate page the locking is
    also unnecessary, with the above caveats.
    
    Partial write of a not uptodate page still needs to be handled.  One way
    would be to read the complete page before doing the write.  This is not
    possible, since it might break filesystems that don't expect any READ
    requests when the file was opened O_WRONLY.
    
    The other solution is to serialize the synchronous write with reads from
    the partial pages.  The easiest way to do this is to keep the partial pages
    locked.  The problem is that a write() may involve two such pages (one head
    and one tail).  This patch fixes it by only locking the partial tail page.
    If there's a partial head page as well, then split that off as a separate
    WRITE request.
    Reported-by: NQian Cai <cai@lca.pw>
    Link: https://lore.kernel.org/linux-fsdevel/4794a3fa3742a5e84fb0f934944204b55730829b.camel@lca.pw/
    Fixes: ea9b9907 ("fuse: implement perform_write")
    Cc: <stable@vger.kernel.org> # v2.6.26
    Signed-off-by: NVivek Goyal <vgoyal@redhat.com>
    Signed-off-by: NMiklos Szeredi <mszeredi@redhat.com>
    Signed-off-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
    Signed-off-by: NChen Jun <chenjun102@huawei.com>
    Acked-by: NWeilong Chen <chenweilong@huawei.com>
    Signed-off-by: NZheng Zengkai <zhengzengkai@huawei.com>
    258c2b71
file.c 85.8 KB