From 70c47311c0a15e574101d6a1eb74919aee070d66 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Mon, 12 Jan 2026 13:51:39 +0000 Subject: [PATCH] Fix assertion failure in blocksize-policy filter Fix v2v conversion failure when minimum_io_size > 64K resolves: RHEL-139390 --- ...g_hexdump.pod-Add-a-link-back-to-nbd.patch | 27 ++ ...-Fix-assertion-failure-on-unaligned-.patch | 253 ++++++++++++++++++ ...tise-minimum_io_size-64K-the-max-sup.patch | 46 ++++ ...out-problems-with-file-plugin-block_.patch | 33 +++ ...ulations-of-block-size-hints-for-blo.patch | 111 ++++++++ nbdkit.spec | 12 +- 6 files changed, 480 insertions(+), 2 deletions(-) create mode 100644 0006-docs-nbdkit_debug_hexdump.pod-Add-a-link-back-to-nbd.patch create mode 100644 0007-blocksize-policy-Fix-assertion-failure-on-unaligned-.patch create mode 100644 0008-file-Don-t-advertise-minimum_io_size-64K-the-max-sup.patch create mode 100644 0009-todo-Add-note-about-problems-with-file-plugin-block_.patch create mode 100644 0010-file-Change-calculations-of-block-size-hints-for-blo.patch diff --git a/0006-docs-nbdkit_debug_hexdump.pod-Add-a-link-back-to-nbd.patch b/0006-docs-nbdkit_debug_hexdump.pod-Add-a-link-back-to-nbd.patch new file mode 100644 index 0000000..02b31f0 --- /dev/null +++ b/0006-docs-nbdkit_debug_hexdump.pod-Add-a-link-back-to-nbd.patch @@ -0,0 +1,27 @@ +From 8dc9b3df1c2ff32a73b8dfd179b84b50de792d95 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Tue, 30 Dec 2025 22:01:04 +0000 +Subject: [PATCH] docs/nbdkit_debug_hexdump.pod: Add a link back to + nbdkit-checkwrite-filter + +Updates: commit 6a1c92f6a30941f8a891e704eeb95296e6d5669a +(cherry picked from commit 674f72a6553b97f86f8f8a276f86354c977d339f) +--- + docs/nbdkit_debug_hexdump.pod | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/docs/nbdkit_debug_hexdump.pod b/docs/nbdkit_debug_hexdump.pod +index 4c4cae19..1a428699 100644 +--- a/docs/nbdkit_debug_hexdump.pod ++++ b/docs/nbdkit_debug_hexdump.pod +@@ -62,6 +62,7 @@ L, + L, + L, + L, ++L, + L, + L, + L. +-- +2.47.3 + diff --git a/0007-blocksize-policy-Fix-assertion-failure-on-unaligned-.patch b/0007-blocksize-policy-Fix-assertion-failure-on-unaligned-.patch new file mode 100644 index 0000000..7aba358 --- /dev/null +++ b/0007-blocksize-policy-Fix-assertion-failure-on-unaligned-.patch @@ -0,0 +1,253 @@ +From 8d82d6228a25693bbf65c50ecd32953f48dcba9e Mon Sep 17 00:00:00 2001 +From: Eric Blake +Date: Mon, 5 Jan 2026 10:43:46 -0600 +Subject: [PATCH] blocksize-policy: Fix assertion failure on unaligned block + status + +The blocksize-policy filter had a minor denial-of-service security +flaw, where a client could trigger the server to die from an assertion +failure by sending an unaligned block status request in violation of +NBD protocol recommendations (see the updated test within the patch +for a sample trigger). Note that libnbd makes it difficult to +trigger, as by default an unaligned request won't be sent to the +server. Additionally, use of blocksize-error-policy=error is not +impacted; and although the blocksize-policy filter defaults to an +error policy of allow, it makes less sense to use the filter in +production without opting in to blocksize-error-policy=error. + +Rather than complicating the blocksize-policy filter to manually munge +its extents requests to an aligned boundary, I opted to instead relax +the server's nbdkit_extents_aligned to support unaligned inputs by +first widening the request to alignment boundaries and then truncating +back to the original offset after at least one aligned extent is +learned. The function still stops at the first unaligned extent, +rather than trying harder to use all of the plugin's underlying +information; I have plans to add a parameter in a later patch to +optionally behave more like nbdkit_extents_full, but wanted this patch +to focus on merely the assertion failure. + +An audit of all callers of nbdkit_extents_aligned shows that only +blocksize-policy was vulnerable; the blocksize and swab filters only +ever pass in aligned values. And while at it, I made the interface +accept a 64-bit count, which makes usage easier when a client widens a +request near the 4G boundary up to an alignment boundary. + +Since the flaw is minor, I've gone ahead and made this patch public. +However, in parallel I am pursuing with Red Hat security on whether a +CVE needs to be assigned. + +Fixes: 82b60fcd ("blocksize-policy: Round extents to match minimum alignment", v1.43.11) +Signed-off-by: Eric Blake +(cherry picked from commit d4fe15c78295be3ec5f162bf41f9b9da8a669689) +--- + docs/nbdkit-filter.pod | 7 ++-- + docs/nbdkit-security.pod | 6 +++ + include/nbdkit-filter.h | 2 +- + server/extents.c | 25 +++++++---- + tests/test-blocksize-policy-extents.sh | 57 ++++++++++++++++++++++++++ + 5 files changed, 85 insertions(+), 12 deletions(-) + +diff --git a/docs/nbdkit-filter.pod b/docs/nbdkit-filter.pod +index 022799f4..1bb01675 100644 +--- a/docs/nbdkit-filter.pod ++++ b/docs/nbdkit-filter.pod +@@ -1043,7 +1043,7 @@ A convenience function is provided to filters only which makes it + easier to ensure that the client only encounters aligned extents. + + int nbdkit_extents_aligned (nbdkit_next *next, +- uint32_t count, uint64_t offset, ++ uint64_t count, uint64_t offset, + uint32_t flags, uint32_t align, + struct nbdkit_extents *extents, int *err); + +@@ -1052,8 +1052,9 @@ obtained, where C is a power of 2. Anywhere the underlying + plugin returns differing extents within C bytes, this function + treats that portion of the disk as a single extent with zero and + sparse status bits determined by the intersection of all underlying +-extents. It is an error to call this function with C or +-C that is not already aligned. ++extents. This function supports unaligned C or C, but ++the given C must begin at C and not have any extents ++added yet. + + =head2 C<.cache> + +diff --git a/docs/nbdkit-security.pod b/docs/nbdkit-security.pod +index 64435231..38887bc6 100644 +--- a/docs/nbdkit-security.pod ++++ b/docs/nbdkit-security.pod +@@ -47,6 +47,12 @@ See the full announcement and links to mitigation, tests and fixes + here: + L + ++=head2 denial of service attack by client sending unaligned block status ++ ++See the patch here: ++L ++Full announcement and links to mitigation coming. ++ + =head1 SEE ALSO + + L. +diff --git a/include/nbdkit-filter.h b/include/nbdkit-filter.h +index ffa7fa5d..aa99af65 100644 +--- a/include/nbdkit-filter.h ++++ b/include/nbdkit-filter.h +@@ -135,7 +135,7 @@ NBDKIT_EXTERN_DECL (struct nbdkit_extents *, nbdkit_extents_full, + NBDKIT_ATTRIBUTE_NONNULL ((1, 5))); + NBDKIT_EXTERN_DECL (int, nbdkit_extents_aligned, + (nbdkit_next *next, +- uint32_t count, uint64_t offset, ++ uint64_t count, uint64_t offset, + uint32_t flags, uint32_t align, + struct nbdkit_extents *extents, int *err) + NBDKIT_ATTRIBUTE_NONNULL ((1, 6, 7))); +diff --git a/server/extents.c b/server/extents.c +index e0a2b224..8e507a94 100644 +--- a/server/extents.c ++++ b/server/extents.c +@@ -213,7 +213,7 @@ nbdkit_add_extent (struct nbdkit_extents *exts, + /* Compute aligned extents on behalf of a filter. */ + NBDKIT_DLL_PUBLIC int + nbdkit_extents_aligned (struct context *next_c, +- uint32_t count, uint64_t offset, ++ uint64_t count, uint64_t offset, + uint32_t flags, uint32_t align, + struct nbdkit_extents *exts, int *err) + { +@@ -222,22 +222,25 @@ nbdkit_extents_aligned (struct context *next_c, + struct nbdkit_extent *e, *e2; + int64_t size; + ++ assert (exts->extents.len == 0); ++ assert (exts->start == offset); ++ + size = next->get_size (next_c); + if (size == -1) { + *err = EIO; + return -1; + } +- assert (IS_ALIGNED (offset, align)); +- assert (IS_ALIGNED (count, align) || offset + count == size); ++ exts->start = ROUND_DOWN (offset, align); ++ count = MIN (ROUND_UP (offset + count, align), size) - exts->start; + + /* Perform an initial query, then scan for the first unaligned extent. */ +- if (next->extents (next_c, count, offset, flags, exts, err) == -1) ++ if (next->extents (next_c, count, exts->start, flags, exts, err) == -1) + return -1; + for (i = 0; i < exts->extents.len; ++i) { + e = &exts->extents.ptr[i]; + if (!IS_ALIGNED (e->length, align)) { + /* If the unalignment is past align, just truncate and return early */ +- if (e->offset + e->length > offset + align) { ++ if (e->offset + e->length > exts->start + align) { + e->length = ROUND_DOWN (e->length, align); + exts->extents.len = i + !!e->length; + exts->next = e->offset + e->length; +@@ -271,13 +274,13 @@ nbdkit_extents_aligned (struct context *next_c, + CLEANUP_EXTENTS_FREE struct nbdkit_extents *extents2 = NULL; + + extents2 = nbdkit_extents_new (e->offset + e->length, +- offset + align); ++ exts->start + align); + if (extents2 == NULL) { + *err = errno; + return -1; + } + if (next->extents (next_c, align - e->length, +- offset + e->length, ++ exts->start + e->length, + flags & ~NBDKIT_FLAG_REQ_ONE, + extents2, err) == -1) + return -1; +@@ -298,7 +301,13 @@ nbdkit_extents_aligned (struct context *next_c, + break; + } + } +- /* Once we get here, all extents are aligned. */ ++ /* Once we get here, all extents are aligned. Trim back to the ++ * original offset if it was unaligned. ++ */ ++ e = &exts->extents.ptr[0]; ++ e->length -= offset - exts->start; ++ e->offset += offset - exts->start; ++ exts->start = offset; + return 0; + } + +diff --git a/tests/test-blocksize-policy-extents.sh b/tests/test-blocksize-policy-extents.sh +index 46f804bb..688161ba 100755 +--- a/tests/test-blocksize-policy-extents.sh ++++ b/tests/test-blocksize-policy-extents.sh +@@ -40,6 +40,8 @@ set -u + requires_run + requires_plugin data + requires_nbdinfo ++requires nbdsh --base-allocation --version ++requires_nbdsh_uri + + files="blocksize-policy-extents.out" + rm -f $files +@@ -69,3 +71,58 @@ diff -u - blocksize-policy-extents.out < blocksize-policy-extents.out ++diff -u - blocksize-policy-extents.out < blocksize-policy-extents.out ++diff -u - blocksize-policy-extents.out < +Date: Wed, 7 Jan 2026 20:25:56 +0000 +Subject: [PATCH] file: Don't advertise minimum_io_size > 64K (the max + supported by NBD) + +If you create an LVM thin pool with chunk size of 128K then the kernel +will advertise /sys/block/dm-X/queue/minimum_io_size as 131072 (128K) +and the file plugin will set the minimum block size to 128K as well. +However the NBD protocol only supports a max of 64K, thus we get the +following failure: + + nbdkit: file[2]: error: plugin must set minimum block size between 1 and 64K + +This is not a hard limit and the kernel will split smaller requests, +so the simplest solution is to round down larger sizes to 64K. + +Fixes: https://issues.redhat.com/browse/RHEL-139390 +Reported-by: Nijin Ashok +Thanks: Eric Blake +See: https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md#size-constraints +(cherry picked from commit 42b72dc751030c1210bc2d50f6ff0c1c1a902afc) +--- + plugins/file/file.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/plugins/file/file.c b/plugins/file/file.c +index 6a8e65c9..c69d837c 100644 +--- a/plugins/file/file.c ++++ b/plugins/file/file.c +@@ -748,6 +748,12 @@ file_open (int readonly) + if (ioctl (h->fd, BLKIOMIN, &minimum_io_size) == -1) + nbdkit_debug ("cannot get BLKIOMIN: %s: %m", h->name); + ++ /* This is the maximum that NBD supports. For Linux devices, ++ * minimum_io_size is only a hint and smaller operations work. ++ */ ++ if (minimum_io_size > 65536) ++ minimum_io_size = 65536; ++ + if (ioctl (h->fd, BLKIOOPT, &optimal_io_size) == -1) + nbdkit_debug ("cannot get BLKIOOPT: %s: %m", h->name); + else if (optimal_io_size == 0) +-- +2.47.3 + diff --git a/0009-todo-Add-note-about-problems-with-file-plugin-block_.patch b/0009-todo-Add-note-about-problems-with-file-plugin-block_.patch new file mode 100644 index 0000000..fa5fc24 --- /dev/null +++ b/0009-todo-Add-note-about-problems-with-file-plugin-block_.patch @@ -0,0 +1,33 @@ +From 5d257a1ef7c742ac90f292c2dd5d74594e3d28ec Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 8 Jan 2026 21:05:23 +0000 +Subject: [PATCH] todo: Add note about problems with file plugin block_size + +(cherry picked from commit 2155ff48364a4d1a7368cf4f43ed78ce1ee47dd3) +--- + TODO.md | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/TODO.md b/TODO.md +index 8488dd95..ff72ed25 100644 +--- a/TODO.md ++++ b/TODO.md +@@ -199,6 +199,15 @@ nbdkit-file-plugin: + - For more, see comments in + https://gitlab.com/nbdkit/nbdkit/-/merge_requests/88 + ++* Block size constraints mapping between Linux hints and the ++ file_block_size callback could be improved. Our correspondent ++ writes: ++ ++ > the kernel's minimum_io_size is a hint, while logical_block_size ++ is the hard limit. nbdkit should probably be setting "minimum" to ++ logical_block_size, and using the ++ max(minimum_io_size,optimal_io_size) for "preferred" ++ + nbdkit-vram-plugin: + + * Investigate why, on AMD Radeon, trim does not immediately free Video +-- +2.47.3 + diff --git a/0010-file-Change-calculations-of-block-size-hints-for-blo.patch b/0010-file-Change-calculations-of-block-size-hints-for-blo.patch new file mode 100644 index 0000000..d086621 --- /dev/null +++ b/0010-file-Change-calculations-of-block-size-hints-for-blo.patch @@ -0,0 +1,111 @@ +From 07cbf8b7a1e94079946c36ab83c1ae75ac175d4e Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 8 Jan 2026 21:38:36 +0000 +Subject: [PATCH] file: Change calculations of block size hints for block + devices + +Related: https://issues.redhat.com/browse/RHEL-139390 +Related: commit 42b72dc751030c1210bc2d50f6ff0c1c1a902afc +Thanks: Eric Blake, Mikulas Patocka +(cherry picked from commit d0ee3c8a6dde446385c9b8e02f8d4623cb4d4184) +--- + TODO.md | 9 --------- + plugins/file/file.c | 45 ++++++++++++++++++++++++++++----------------- + 2 files changed, 28 insertions(+), 26 deletions(-) + +diff --git a/TODO.md b/TODO.md +index ff72ed25..8488dd95 100644 +--- a/TODO.md ++++ b/TODO.md +@@ -199,15 +199,6 @@ nbdkit-file-plugin: + - For more, see comments in + https://gitlab.com/nbdkit/nbdkit/-/merge_requests/88 + +-* Block size constraints mapping between Linux hints and the +- file_block_size callback could be improved. Our correspondent +- writes: +- +- > the kernel's minimum_io_size is a hint, while logical_block_size +- is the hard limit. nbdkit should probably be setting "minimum" to +- logical_block_size, and using the +- max(minimum_io_size,optimal_io_size) for "preferred" +- + nbdkit-vram-plugin: + + * Investigate why, on AMD Radeon, trim does not immediately free Video +diff --git a/plugins/file/file.c b/plugins/file/file.c +index c69d837c..abd78ffc 100644 +--- a/plugins/file/file.c ++++ b/plugins/file/file.c +@@ -414,7 +414,7 @@ file_config_complete (void) + static void + file_dump_plugin (void) + { +-#if defined(BLKIOMIN) && defined(BLKIOOPT) ++#if defined(BLKSSZGET) && defined(BLKIOMIN) && defined(BLKIOOPT) + printf ("file_block_size=yes\n"); + #endif + #ifdef BLKROTATIONAL +@@ -741,32 +741,43 @@ file_open (int readonly) + #endif + + h->minimum = h->preferred = h->maximum = 0; +-#if defined(BLKIOMIN) && defined(BLKIOOPT) ++#if defined(BLKSSZGET) && defined(BLKIOMIN) && defined(BLKIOOPT) + if (h->is_block_device) { +- unsigned int minimum_io_size = 0, optimal_io_size = 0; ++ int logical_block_size = 0; ++ unsigned minimum_io_size = 0, optimal_io_size = 0; ++ ++ if (ioctl (h->fd, BLKSSZGET, &logical_block_size) == -1) ++ nbdkit_debug ("ioctl: %s: %s: %m", "BLKSSZGET", h->name); + + if (ioctl (h->fd, BLKIOMIN, &minimum_io_size) == -1) +- nbdkit_debug ("cannot get BLKIOMIN: %s: %m", h->name); +- +- /* This is the maximum that NBD supports. For Linux devices, +- * minimum_io_size is only a hint and smaller operations work. +- */ +- if (minimum_io_size > 65536) +- minimum_io_size = 65536; ++ nbdkit_debug ("ioctl: %s: %s: %m", "BLKIOMIN", h->name); + + if (ioctl (h->fd, BLKIOOPT, &optimal_io_size) == -1) +- nbdkit_debug ("cannot get BLKIOOPT: %s: %m", h->name); ++ nbdkit_debug ("ioctl: %s: %s: %m", "BLKIOOPT", h->name); + else if (optimal_io_size == 0) + /* All devices in the Linux kernel except for MD report optimal +- * as 0. In that case guess a good value. ++ * as 0. In that case use logical_block_size. + */ +- optimal_io_size = MAX (minimum_io_size, 4096); ++ optimal_io_size = logical_block_size; + + /* Check the values are sane before using them. */ +- if (minimum_io_size >= 512 && is_power_of_2 (minimum_io_size) && +- optimal_io_size >= minimum_io_size && is_power_of_2 (optimal_io_size)) { +- h->minimum = minimum_io_size; +- h->preferred = optimal_io_size; ++ if (logical_block_size >= 512 && is_power_of_2 (logical_block_size) && ++ minimum_io_size >= 512 && is_power_of_2 (minimum_io_size) && ++ minimum_io_size >= logical_block_size && ++ optimal_io_size >= 512 && is_power_of_2 (optimal_io_size) && ++ optimal_io_size >= logical_block_size) { ++ /* The mapping from Linux kernel settings to NBD protocol block ++ * sizes is not obvious. logical_block_size is the sector size, ++ * and anything smaller than this will cause a RMW cycle. For ++ * the preferred size, we are advised to use the largest of ++ * minimum_io_size and optimal_io_size. For this plugin maximum ++ * can be the largest that NBD can handle. ++ */ ++ ++ /* 64K is the largest minimum that the NBD protocol supports. */ ++ h->minimum = MIN (65536, logical_block_size); ++ ++ h->preferred = MAX (minimum_io_size, optimal_io_size); + h->maximum = 0xffffffff; + } + } +-- +2.47.3 + diff --git a/nbdkit.spec b/nbdkit.spec index fb302fa..9a9b508 100644 --- a/nbdkit.spec +++ b/nbdkit.spec @@ -55,7 +55,7 @@ Name: nbdkit Version: 1.46.1 -Release: 1%{?dist} +Release: 2%{?dist} Summary: NBD server License: BSD-3-Clause @@ -85,6 +85,11 @@ Patch0002: 0002-python-Sort-documentation-for-module-functions-in-or.patch Patch0003: 0003-server-Add-nbdkit_debug_hexdiff-function.patch Patch0004: 0004-checkwrite-Display-differences-if-D-checkwrite.showd.patch Patch0005: 0005-docs-nbdkit_debug_hexdump.pod-Document-when-hexdiff-.patch +Patch0006: 0006-docs-nbdkit_debug_hexdump.pod-Add-a-link-back-to-nbd.patch +Patch0007: 0007-blocksize-policy-Fix-assertion-failure-on-unaligned-.patch +Patch0008: 0008-file-Don-t-advertise-minimum_io_size-64K-the-max-sup.patch +Patch0009: 0009-todo-Add-note-about-problems-with-file-plugin-block_.patch +Patch0010: 0010-file-Change-calculations-of-block-size-hints-for-blo.patch # For automatic RPM Provides generation. # See: https://rpm-software-management.github.io/rpm/manual/dependency_generators.html @@ -1592,13 +1597,16 @@ fi %changelog -* Sun Jan 04 2026 Richard W.M. Jones - 1.46.1-1 +* Mon Jan 12 2026 Richard W.M. Jones - 1.46.1-2 - Rebase to nbdkit 1.46.1 - Backport nbdkit_debug_hexdiff from nbdkit 1.47. resolves: RHEL-111242 - Synchronize spec file with Fedora. - vddk: Don't use FNM_PATHNAME when matching export parameter resolves: RHEL-122755 +- Fix assertion failure in blocksize-policy filter +- Fix v2v conversion failure when minimum_io_size > 64K + resolves: RHEL-139390 * Wed Jul 09 2025 Richard W.M. Jones - 1.44.1-2 - Rebase to nbdkit 1.44.1