From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-qk1-f176.google.com (mail-qk1-f176.google.com [209.85.222.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3AB902857FA for ; Fri, 19 Jun 2026 13:58:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.176 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781877490; cv=none; b=apaosXQRA6aPzGMCJuY/1RiL6JhUOuWSeZ8lReFpAxv1VZ4cmGl0RjCA+0ivKAqpZa5ISoq1OYWB9bLQnUqkkcpGC76za+HI8Fu9A8AugALrlwINgZh7tkmnZjd3gRy73jVl3im6rviW7mBLW+vgpNJ9tntiy65fBqxEhFbmx5g= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781877490; c=relaxed/simple; bh=omtYFf4n2B3tJ5JX1EKUJJ9+OTg+njF8ZEge8NqFLR8=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=mEOZ1aoI/BHXE50XO2Bz6YJ8SV4L+dh8tkTL3MAClJXAMsOlwTq0GlDhtR60pd7JFkTSVBTqJXWOFnqMGKbBgSYl4hYjVLNupWpCmZk3TVjMhRKGnQ+IHJgxfzRBdTUcECJfAMHkWBSjKb1BeiXeOfyw4LLerpBywVBh8CkuXBM= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=kp0sruLa; arc=none smtp.client-ip=209.85.222.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="kp0sruLa" Received: by mail-qk1-f176.google.com with SMTP id af79cd13be357-9157b94a07aso227282985a.0 for ; Fri, 19 Jun 2026 06:58:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1781877488; x=1782482288; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=Uz49zhpiaGZXd32cBhi50h5xA43i0wvUuJPXu8fbVQ8=; b=kp0sruLabhv3AT52xI7j0jMFmSPyqrMQh92k2OyrVnpRfIXji1aFK/M/+we6+WrriH 7OYhhDNIFywkmBc762j4rYEAL6gJLa86lq63QT/bu5ke/QMghYQMCmugb4DD+N5Y28A3 jcjXojlktFux+jgdktnuPYPCJOe/UjeGviaTsqIQ+YK884MDO+Fk9KfHrr3fcG3OKKeR JLRkHsiTDvySCJk+f7YAl2fbHazkBMsvt6aUueQCFkuou4Y4vVAcmgKAMSNjbn2TzCfs ORpmXrJGeKUhlktoQ1l1Q2OWxHSBh5mTjxPRxfxWXIvSHbWuYGZ1E56MX8rBVSnMfUMs U8RQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781877488; x=1782482288; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=Uz49zhpiaGZXd32cBhi50h5xA43i0wvUuJPXu8fbVQ8=; b=Zktcm0oRRVG5YLMdTTLbpvt9TTpYXQ36IQ2ApVVh8aKu0Z3SotdJLUaagF/LU7AAfe ltTKFvkKOolE5SiqpUIXL3E9k3dEqrJ4ctdOF9dq0hYhwZBYX+kPYEhrd4nZmse7ZkoT ZsjBPIlnqqF6T8JpsYaAMNWU2Ve/MIVNyZoVb0dFDP9DMHJAn4ZF+mSCjM8pHRN+8bih f/N8eYkFOtPRRoRa9VmgXoMn3SAC7fX4NUggGKGlhBv7bMlcQILrgL0Q/EeAA4Z9ycP7 jPQ8cSMO9jzhYGnlDzyCxm7ICVqcCnfWvEa3VMtuoRh0kkqR90H/4jEB3IPv6mlky6IY rq1Q== X-Forwarded-Encrypted: i=1; AFNElJ8ppw3PyDUZCpqgkN8ZeUGk03Fqk71Lu4+hkwEjtfautTrhximXrL2kvhcWMZVhRVRsThC5gx8FEOyYV1k=@vger.kernel.org X-Gm-Message-State: AOJu0YzGyWjF0UnM6G7MzeEbZMBTsSe2/2RCllIy9BQxlG0XFYxXRNkR C+H/GiLfSZEGEN/U+CVqDTjFXYjiP2jXLeUuIQupCpzYjXln30O4AgbJ X-Gm-Gg: AfdE7cmA1e02+SCfT5eqOeGz6ULBF/rpfAwMJqmx5+musmwL56JlUXrHP8Yh384YA0f huoUe50iVZ8nXUMxwEMWaBxzhij4HL3EJaierEm8g3AVWNOcQRMVvxBXif8NOilEhZR20YjkRj+ /EBPqOJouk91avJQutvvqLSR/DNetPDz63Itbpt9MQQqvtgUsa+FI+/BMpSaOqdrluQeTHal5G+ BnimWFhMNyJaNQg/mjI79aY6hxTb+/urMv3+Gd4KdQ3gp6KhSIYmvoiwmOVQWj72ZfoAmhwg6/A xFrnfbh2zBdCLTJgwxbqIHvQcQquFjGH7hnf73KLn4OjHX9RAa51R1aZF4f/Q10NUL+DgLRKm0e fOrum/qKsw2zzQfFPBOS/4stSQJ+tbcnGvcxI/VNEFcINcQeeglSx7l5y2UnfZA/PdPfqpWvyrA Z5YVox/5Ss0ZycWiciq875eCq3wk8k+yuGIb5i4Xl0+3dDu89E+W+4gZYA7/2/Frb58asMV8x5O EXfAo+JHijwyv4zYfR3LUNqIhzuPdgh X-Received: by 2002:a05:620a:29c6:b0:914:7e9a:2716 with SMTP id af79cd13be357-920d46198e3mr392178685a.38.1781877488024; Fri, 19 Jun 2026 06:58:08 -0700 (PDT) Received: from server0.tail6e7dd.ts.net (c-68-48-65-54.hsd1.mi.comcast.net. [68.48.65.54]) by smtp.gmail.com with ESMTPSA id af79cd13be357-920a142f50asm248618785a.12.2026.06.19.06.58.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 19 Jun 2026 06:58:07 -0700 (PDT) From: Michael Bommarito To: Gao Xiang , Chao Yu Cc: Yue Hu , Jeffle Xu , Sandeep Dhavale , linux-erofs@lists.ozlabs.org, linux-kernel@vger.kernel.org Subject: [PATCH] erofs: complete fscache pseudo-bio once when a read is split Date: Fri, 19 Jun 2026 09:58:00 -0400 Message-ID: <20260619135800.1594811-1-michael.bommarito@gmail.com> X-Mailer: git-send-email 2.53.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 7bit In fscache mode a compressed read uses one pseudo-bio whose io->end_io is erofs_fscache_bio_endio(). When prepare_ondemand_read() splits the read at a cached/uncached boundary, erofs_fscache_read_io_async() issues several fscache subreads on the same bio and erofs_fscache_bio_endio() calls bio_endio() on each. The pseudo-bio is not chained, so z_erofs_endio() runs once per subread while z_erofs_submit_queue() counted the bio only once, underflowing pending_bios: the reader hangs in D state, or, on async completion, the first completion frees the decompress queue and the rest are use-after-free. Hold a bio_inc_remaining() reference per issued subread and drop the submitter's initial reference with one bio_endio() once submission finishes, so the bio completes exactly once. The request path (erofs_fscache_req_end_io) is unaffected; it uses its own refcount and never calls bio_endio(). Fixes: a1bafc3109d7 ("erofs: support compressed inodes over fscache") Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 --- Reproduced on x86-64 with KASAN via the erofs-on-demand path (a cachefiles ondemand daemon serving a crafted compressed image that splits a pcluster read). Found with the help of an automated review tool. Without this patch a stock kernel either hangs the reader: task:dd state:D filemap_get_pages / erofs_file_read_iter or, when completion is asynchronous, faults: BUG: KASAN: slab-use-after-free in z_erofs_endio Kernel panic - not syncing: Fatal exception in interrupt With this patch the same daemon, image and reads complete cleanly: no hang, no KASAN report, no panic. Harness and full logs available on request. fs/erofs/fscache.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/fs/erofs/fscache.c b/fs/erofs/fscache.c index 685c68774379b..0cce498faa32d 100644 --- a/fs/erofs/fscache.c +++ b/fs/erofs/fscache.c @@ -29,6 +29,14 @@ struct erofs_fscache_rq { refcount_t ref; }; +struct erofs_fscache_bio { + struct erofs_fscache_io io; + struct bio bio; /* w/o bdev to share bio_add_page/endio() */ + struct bio_vec bvecs[BIO_MAX_VECS]; +}; + +static void erofs_fscache_bio_endio(void *priv, ssize_t transferred_or_error); + static bool erofs_fscache_io_put(struct erofs_fscache_io *io) { if (!refcount_dec_and_test(&io->ref)) @@ -122,8 +130,13 @@ static int erofs_fscache_read_io_async(struct fscache_cookie *cookie, enum netfs_io_source source; struct netfs_cache_resources *cres = &io->cres; struct iov_iter *iter = &io->iter; + struct bio *bio = NULL; int ret; + /* Chain-account a split pseudo-bio so it completes only once. */ + if (io->end_io == erofs_fscache_bio_endio) + bio = &container_of(io, struct erofs_fscache_bio, io)->bio; + ret = fscache_begin_read_operation(cres, cookie); if (ret) return ret; @@ -143,6 +156,8 @@ static int erofs_fscache_read_io_async(struct fscache_cookie *cookie, iov_iter_truncate(iter, len); refcount_inc(&io->ref); + if (bio) + bio_inc_remaining(bio); ret = fscache_read(cres, pstart, iter, NETFS_READ_HOLE_FAIL, io->end_io, io); if (ret == -EIOCBQUEUED) @@ -160,12 +175,6 @@ static int erofs_fscache_read_io_async(struct fscache_cookie *cookie, return 0; } -struct erofs_fscache_bio { - struct erofs_fscache_io io; - struct bio bio; /* w/o bdev to share bio_add_page/endio() */ - struct bio_vec bvecs[BIO_MAX_VECS]; -}; - static void erofs_fscache_bio_endio(void *priv, ssize_t transferred_or_error) { struct erofs_fscache_bio *io = priv; @@ -200,9 +209,9 @@ void erofs_fscache_submit_bio(struct bio *bio) ret = erofs_fscache_read_io_async(io->io.private, bio->bi_iter.bi_sector << 9, &io->io); erofs_fscache_io_put(&io->io); - if (!ret) - return; - bio->bi_status = errno_to_blk_status(ret); + if (ret) + bio->bi_status = errno_to_blk_status(ret); + /* Drop the submitter's ref; subread chain refs complete the bio. */ bio_endio(bio); } -- 2.53.0