From nobody Tue Apr 7 21:45:14 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1773242858; cv=none; d=zohomail.com; s=zohoarc; b=jIDOFydzdaH2Qfu3no1C0pz5COC1XIU2oKVkKddXqDMTsb3+Bk+elb5upAKN6x1Cuz9awzuhmQmMEa6ZUKqFi+doFjPU7kfSzUs9PwAZMmFNDecExDRpgVV+1d2x3FK+t9XO7YHRs+gQs1bqeK4bqrRmc866KcaFY6539Kquqek= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773242858; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=inDUBod4rxifPap/EUG2mcJZ5KFZ5p0VexH6iiPz9eA=; b=Ad/8a+mIsBNA3izdkjzQXWVvYHSpnoYFWqBeFYmbs3mvTAS7fQGM9vnhf79dvfG5mRhRJ7yIefJQoEfLyoIaX0EynPaHhY0xbnrx91qpdM9z2Gcm83EEVSX97ZIwfexJ2jOQV8J0cgrPVaDxcLVTP3QPtT2Xgc8zP8ne5hA24AM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773242858335854.3203510668866; Wed, 11 Mar 2026 08:27:38 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w0LEO-0007ln-5R; Wed, 11 Mar 2026 11:12:16 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w0LDb-0004AB-2r; Wed, 11 Mar 2026 11:11:34 -0400 Received: from isrv.corpit.ru ([212.248.84.144]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w0LDZ-0006D7-Cj; Wed, 11 Mar 2026 11:11:26 -0400 Received: from tsrv.corpit.ru (tsrv.tls.msk.ru [192.168.177.2]) by isrv.corpit.ru (Postfix) with ESMTP id 61D84191EB1; Wed, 11 Mar 2026 18:04:44 +0300 (MSK) Received: from think4mjt.tls.msk.ru (mjtthink.wg.tls.msk.ru [192.168.177.146]) by tsrv.corpit.ru (Postfix) with ESMTP id 4DE0637C2F2; Wed, 11 Mar 2026 18:05:22 +0300 (MSK) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tls.msk.ru; s=202602; t=1773241484; bh=UhDEF+0FkvE8wwY7FcHd1IRaCkhOlhAOXEZlZtt+UPQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=NEj0UaiBdlMKTDrHDibW/N5BOmGGNOZ/s2E/1vEDbCDgbPz7ey8DHX1bOTGWDzJiJ 8uQFao80sU+M72sObg7HMIOnVgYfFaG+S+rq2gM5C3CjekmfqQFgCInSkYQQBEzkr1 rpeS+lqteRbc/Yy/mJmv9zHiJOI8+NUl/STqGrIYd7m3ULMFm0i0Kh0FTKvChS1cNA KRS4YRGy1/1zT3h2eUTyRHh997pOMq/hTj3WyiVA0LvgegDEanO7/I5uvCPWUO2+Yj QACPXGRrcYfPzmFsPO7lr1dlQHK2LQ+h+5GNWzUUrCmHeJb3aKYTZkH+bmKUvkw3Nj LtZM/Eu7aZEuQ== From: Michael Tokarev To: qemu-devel@nongnu.org Cc: qemu-stable@nongnu.org, Hanna Czenczek , Kevin Wolf , Michael Tokarev Subject: [Stable-10.1.5 46/46] fuse: Copy write buffer content before polling Date: Wed, 11 Mar 2026 18:03:22 +0300 Message-ID: <20260311150327.1084669-46-mjt@tls.msk.ru> X-Mailer: git-send-email 2.47.3 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=212.248.84.144; envelope-from=mjt@tls.msk.ru; helo=isrv.corpit.ru X-Spam_score_int: -2 X-Spam_score: -0.3 X-Spam_bar: / X-Spam_report: (-0.3 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @tls.msk.ru) X-ZM-MESSAGEID: 1773242858492158500 Content-Type: text/plain; charset="utf-8" From: Hanna Czenczek aio_poll() in I/O functions can lead to nested read_from_fuse_export() calls, overwriting the request buffer's content. The only function affected by this is fuse_write(), which therefore must use a bounce buffer or corruption may occur. Note that in addition we do not know whether libfuse-internal structures can cope with this nesting, and even if we did, we probably cannot rely on it in the future. This is the main reason why we want to remove libfuse from the I/O path. I do not have a good reproducer for this other than: $ dd if=3D/dev/urandom of=3Dimage bs=3D1M count=3D4096 $ dd if=3D/dev/zero of=3Dcopy bs=3D1M count=3D4096 $ touch fuse-export $ qemu-storage-daemon \ --blockdev file,node-name=3Dfile,filename=3Dcopy \ --export \ fuse,id=3Dexp,node-name=3Dfile,mountpoint=3Dfuse-export,writable=3Dtrue= \ & Other shell: $ qemu-img convert -p -n -f raw -O raw -t none image fuse-export $ killall -SIGINT qemu-storage-daemon $ qemu-img compare image copy Content mismatch at offset 0! (The -t none in qemu-img convert is important.) I tried reproducing this with throttle and small aio_write requests from another qemu-io instance, but for some reason all requests are perfectly serialized then. I think in theory we should get parallel writes only if we set fi->parallel_direct_writes in fuse_open(). In fact, I can confirm that if we do that, that throttle-based reproducer works (i.e. does get parallel (nested) write requests). I have no idea why we still get parallel requests with qemu-img convert anyway. Also, a later patch in this series will set fi->parallel_direct_writes and note that it makes basically no difference when running fio on the current libfuse-based version of our code. It does make a difference without libfuse. So something quite fishy is going on. I will try to investigate further what the root cause is, but I think for now let's assume that calling blk_pwrite() can invalidate the buffer contents through nested polling. Cc: qemu-stable@nongnu.org Reviewed-by: Kevin Wolf Signed-off-by: Hanna Czenczek Message-ID: <20260309150856.26800-2-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf (cherry picked from commit a3fcbca0ef643a8aecf354bdeb08b1d81e5b33e7) Signed-off-by: Michael Tokarev diff --git a/block/export/fuse.c b/block/export/fuse.c index 465cc9891d..aec4d8736d 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; } =20 + /* + * Note that aio_poll() in any request-processing function can lead to= a + * nested read_from_fuse_export() call, which will overwrite the conte= nts of + * exp->fuse_buf. Anything that takes a buffer needs to take care tha= t the + * content is copied before potentially polling via aio_poll(). + */ fuse_session_process_buf(exp->fuse_session, &exp->fuse_buf); =20 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 *f= i) { FuseExport *exp =3D fuse_req_userdata(req); + QEMU_AUTO_VFREE void *copied =3D NULL; int64_t length; int ret; =20 @@ -638,6 +645,14 @@ static void fuse_write(fuse_req_t req, fuse_ino_t inod= e, const char *buf, return; } =20 + /* + * Heed the note on read_from_fuse_export(): If we call aio_poll() (wh= ich + * any blk_*() I/O function may do), read_from_fuse_export() may be ne= sted, + * overwriting the request buffer content. Therefore, we must copy it= here. + */ + copied =3D 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, } } =20 - ret =3D blk_pwrite(exp->common.blk, offset, size, buf, 0); + ret =3D blk_pwrite(exp->common.blk, offset, size, copied, 0); if (ret >=3D 0) { fuse_reply_write(req, size); } else { --=20 2.47.3