diff --git a/block/export/fuse.c b/block/export/fuse.c index 8cf4572f78..cea9de61f1 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -301,6 +301,12 @@ static void read_from_fuse_export(void *opaque) goto out; } + /* + * Note that aio_poll() in any request-processing function can lead to a + * nested read_from_fuse_export() call, which will overwrite the contents of + * exp->fuse_buf. Anything that takes a buffer needs to take care that the + * content is copied before potentially polling via aio_poll(). + */ fuse_session_process_buf(exp->fuse_session, &exp->fuse_buf); out: @@ -624,6 +630,7 @@ static void fuse_write(fuse_req_t req, fuse_ino_t inode, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { FuseExport *exp = fuse_req_userdata(req); + QEMU_AUTO_VFREE void *copied = NULL; int64_t length; int ret; @@ -638,6 +645,14 @@ static void fuse_write(fuse_req_t req, fuse_ino_t inode, const char *buf, return; } + /* + * Heed the note on read_from_fuse_export(): If we call aio_poll() (which + * any blk_*() I/O function may do), read_from_fuse_export() may be nested, + * overwriting the request buffer content. Therefore, we must copy it here. + */ + copied = blk_blockalign(exp->common.blk, size); + memcpy(copied, buf, size); + /** * Clients will expect short writes at EOF, so we have to limit * offset+size to the image length. @@ -660,7 +675,7 @@ static void fuse_write(fuse_req_t req, fuse_ino_t inode, const char *buf, } } - ret = blk_pwrite(exp->common.blk, offset, size, buf, 0); + ret = blk_pwrite(exp->common.blk, offset, size, copied, 0); if (ret >= 0) { fuse_reply_write(req, size); } else {