From: Charles Pellegrini <c4ffein.work@gmail.com>
To: akpm@linux-foundation.org, egorenar-dev@posteo.net,
robert.jarzmik@free.fr, t-pratham@ti.com, david@davidgow.net,
linux-kernel@vger.kernel.org
Subject: [PATCH v2 0/5] lib: scatterlist: fix sg_split() partial-coverage geometry, two latent corruption bugs, and add KUnit tests
Date: Wed, 10 Jun 2026 15:39:32 -0700 (PDT) [thread overview]
Message-ID: <178113124323.90620.6403136846887207198@gmail.com> (raw)
In-Reply-To: <178027099087.72481.1976843064458686851@gmail.com>
v1 was a 2-patch series (the overshoot fix + a KUnit suite). Andrew's
review asked whether this was seen in real life -- it was not; it came
from property-based testing, not a field report -- and pointed at an
automated review (Sashiko) that flagged a pre-existing OOB and a bug in
the test. Chasing those turned up a third, separate latent bug. v2
addresses all of it.
sg_split() is EXPORT_SYMBOL'd, so its contract is "any caller, any
split_sizes[]", not just the six in-tree call sites. None of the three
bugs below is triggered by an in-tree caller today -- all are latent. So
even without a real-world trigger, I think they're worth fixing.
The three bugs (all Fixes: f8bcbe62acd0):
1. sg_calculate_split() nb_splits overshoot (the v1 fix, unchanged).
Partial coverage ending mid-entry of a non-last input entry overshoots
the counter to -1, misses the !nb_splits termination check, and folds a
trailing entry into the last split.
2. sg_split_phys() ->length clobber. On !NEED_SG_DMA_LENGTH arches
sg_dma_len() aliases ->length, so the DMA-scrub line zeroes the CPU
length it has just computed; only the last entry is restored, so the
non-last entries of a multi-entry split keep length 0 -> silent short
data. Found while correcting the test argument below.
3. zero-nents ZERO_SIZE_PTR OOB. A trailing zero-size split that receives
no input entry leaves out_sg == ZERO_SIZE_PTR; the out_sg[-1].length
write is then out-of-bounds (KASAN splat). Flagged by the automated
review; confirmed with KASAN and a userspace ASAN harness.
Plus a test-argument fix folded into the suite: v1 passed in_mapped_nents
= n_in for an unmapped list, where it must be 0. On NEED arches that drove
the mapped pass to read a zeroed dma_length and return -EINVAL, so the
suite would fail there (it passed on UML only because UML aliases dma_len
onto ->length). Correcting it is what exposed bug 2.
Patch layout (bisect-safe):
1/5 overshoot fix -- unchanged from v1
2/5 ->length clobber fix -- bug 2; lands before the test that
exposes it
3/5 KUnit suite -- corrected in_mapped_nents; each case
run unmapped AND identity-mapped;
DMA-address + end-marker assertions;
NEED-gated divergent-geometry cases
4/5 zero-nents OOB guard -- bug 3
5/5 zero-nents regression test -- guards 4/5
Patches 4 and 5 are an isolable tail: if you'd rather treat a malformed
zero-size split as the caller's problem, they drop cleanly without
touching 1-3. On that bug I went tolerate-and-skip rather than rejecting
with -EINVAL, reasoning it's the droppable tail of the request -- trivial
to respin to -EINVAL if you prefer the stricter contract.
Testing. The suite runs each case both unmapped and identity-mapped
(dma_len == length, contiguous IOVA) on all arches, plus NEED-gated
divergent CPU/DMA geometry (coalescing) cases exercised on x86_64 with
KASAN. Results: UML (!NEED) 17 passed / 3 skipped; x86_64 + KASAN 20/20.
With bug 2 reverted, six unmapped multi-entry cases fail while their
mapped variants pass, so the suite catches it. A real dma_map_sg / IOMMU
rig is still deferred; the mapped coverage here is synthetic (identity
mapping + injected coalescing).
v1: https://lore.kernel.org/all/178027099087.72481.1976843064458686851@gmail.com/
Changes since v1:
- corrected the KUnit in_mapped_nents argument (0 for unmapped lists)
- new: sg_split_phys() ->length clobber fix (bug 2)
- new: zero-nents OOB guard + its regression test (bug 3)
- tests now run unmapped AND identity-mapped, with DMA-address and
end-marker assertions and NEED-gated divergent-geometry cases
- test file moved to lib/tests/sg_split_kunit.c (modern location);
config moved to lib/Kconfig.debug, Makefile line to lib/tests/Makefile
Charles Pellegrini (5):
lib: scatterlist: fix sg_calculate_split() nb_splits overshoot on
partial coverage
lib: scatterlist: fix sg_split_phys() ->length clobber on
!NEED_SG_DMA_LENGTH
lib: scatterlist: add KUnit tests for sg_split()
lib: scatterlist: guard sg_split_phys()/sg_split_mapped() against
zero-nents splits
lib: scatterlist: add zero-nents regression test for sg_split()
lib/Kconfig.debug | 14 +
lib/sg_split.c | 15 +-
lib/tests/Makefile | 1 +
lib/tests/sg_split_kunit.c | 526 +++++++++++++++++++++++++++++++++++++
4 files changed, 552 insertions(+), 4 deletions(-)
create mode 100644 lib/tests/sg_split_kunit.c
--
2.47.3
next prev parent reply other threads:[~2026-06-10 22:39 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-31 23:42 [PATCH 0/2] lib: scatterlist: fix sg_split() partial-coverage geometry + " Charles Pellegrini
2026-05-31 23:42 ` [PATCH 1/2] lib: scatterlist: fix sg_calculate_split() nb_splits overshoot on partial coverage Charles Pellegrini
2026-05-31 23:42 ` [PATCH 2/2] lib: scatterlist: add KUnit tests for sg_split() Charles Pellegrini
2026-06-02 0:55 ` [PATCH 0/2] lib: scatterlist: fix sg_split() partial-coverage geometry + add KUnit tests Andrew Morton
2026-06-10 22:35 ` c4ffein.work
2026-06-10 22:39 ` Charles Pellegrini [this message]
2026-06-10 22:39 ` [PATCH v2 2/5] lib: scatterlist: fix sg_split_phys() ->length clobber on !NEED_SG_DMA_LENGTH Charles Pellegrini
2026-06-10 22:39 ` [PATCH v2 1/5] lib: scatterlist: fix sg_calculate_split() nb_splits overshoot on partial coverage Charles Pellegrini
2026-06-10 22:39 ` [PATCH v2 3/5] lib: scatterlist: add KUnit tests for sg_split() Charles Pellegrini
2026-06-10 22:39 ` [PATCH v2 4/5] lib: scatterlist: guard sg_split_phys()/sg_split_mapped() against zero-nents splits Charles Pellegrini
2026-06-10 22:39 ` [PATCH v2 5/5] lib: scatterlist: add zero-nents regression test for sg_split() Charles Pellegrini
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=178113124323.90620.6403136846887207198@gmail.com \
--to=c4ffein.work@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=david@davidgow.net \
--cc=egorenar-dev@posteo.net \
--cc=linux-kernel@vger.kernel.org \
--cc=robert.jarzmik@free.fr \
--cc=t-pratham@ti.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Powered by JetHome