diff --git a/.gitignore b/.gitignore index c060d61..2f4c71e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ SOURCES/libguestfs.keyring -SOURCES/libnbd-1.2.2.tar.gz +SOURCES/libnbd-1.6.0.tar.gz diff --git a/.libnbd.metadata b/.libnbd.metadata index 33fa200..bcedfd1 100644 --- a/.libnbd.metadata +++ b/.libnbd.metadata @@ -1,2 +1,2 @@ 1bbc40f501a7fef9eef2a39b701a71aee2fea7c4 SOURCES/libguestfs.keyring -68e213e85346cc7b9c390e2a4916c7b3f30345e1 SOURCES/libnbd-1.2.2.tar.gz +b14ac9349d324df71d26cf3de9fb606c56f18cb0 SOURCES/libnbd-1.6.0.tar.gz diff --git a/SOURCES/0001-copy-copy-nbd-to-sparse-file.sh-Skip-test-unless-nbd.patch b/SOURCES/0001-copy-copy-nbd-to-sparse-file.sh-Skip-test-unless-nbd.patch new file mode 100644 index 0000000..173aae4 --- /dev/null +++ b/SOURCES/0001-copy-copy-nbd-to-sparse-file.sh-Skip-test-unless-nbd.patch @@ -0,0 +1,30 @@ +From 486799e853aa9df034366303230a1785087a507a Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Fri, 8 Jan 2021 12:14:18 +0000 +Subject: [PATCH] copy/copy-nbd-to-sparse-file.sh: Skip test unless nbdkit + available. + +This test used nbdkit without checking it is available, which broke +the test on RHEL 8 i686. + +Fixes: commit 28fe8d9d8d1ecb491070d20f22e2f34bb147f19f +(cherry picked from commit 781cb44b63a87f2d5f40590ab8c446ad2e7b6702) +--- + copy/copy-nbd-to-sparse-file.sh | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/copy/copy-nbd-to-sparse-file.sh b/copy/copy-nbd-to-sparse-file.sh +index aa2cb1b..47ff09a 100755 +--- a/copy/copy-nbd-to-sparse-file.sh ++++ b/copy/copy-nbd-to-sparse-file.sh +@@ -24,6 +24,7 @@ set -x + requires cmp --version + requires dd --version + requires dd oflag=seek_bytes +Date: Thu, 4 Feb 2021 17:57:06 +0000 +Subject: [PATCH] generator: Refactor CONNECT.START state. + +Small, neutral refactoring to the CONNECT.START to make the subsequent +commit easier. + +(cherry picked from commit cd231fd94bbfaacdd9b89e7d355ba2bbc83c2aeb) +--- + generator/states-connect.c | 21 ++++++++++----------- + 1 file changed, 10 insertions(+), 11 deletions(-) + +diff --git a/generator/states-connect.c b/generator/states-connect.c +index 392879d..03b34c7 100644 +--- a/generator/states-connect.c ++++ b/generator/states-connect.c +@@ -47,11 +47,12 @@ disable_nagle (int sock) + + STATE_MACHINE { + CONNECT.START: +- int fd; ++ sa_family_t family; ++ int fd, r; + + assert (!h->sock); +- fd = socket (h->connaddr.ss_family, +- SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); ++ family = h->connaddr.ss_family; ++ fd = socket (family, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); + if (fd == -1) { + SET_NEXT_STATE (%.DEAD); + set_error (errno, "socket"); +@@ -65,14 +66,12 @@ STATE_MACHINE { + + disable_nagle (fd); + +- if (connect (fd, (struct sockaddr *) &h->connaddr, +- h->connaddrlen) == -1) { +- if (errno != EINPROGRESS) { +- SET_NEXT_STATE (%.DEAD); +- set_error (errno, "connect"); +- return 0; +- } +- } ++ r = connect (fd, (struct sockaddr *) &h->connaddr, h->connaddrlen); ++ if (r == 0 || (r == -1 && errno == EINPROGRESS)) ++ return 0; ++ assert (r == -1); ++ SET_NEXT_STATE (%.DEAD); ++ set_error (errno, "connect"); + return 0; + + CONNECT.CONNECTING: +-- +2.31.1 + diff --git a/SOURCES/0003-generator-Print-a-better-error-message-if-connect-2-.patch b/SOURCES/0003-generator-Print-a-better-error-message-if-connect-2-.patch new file mode 100644 index 0000000..6ac2a69 --- /dev/null +++ b/SOURCES/0003-generator-Print-a-better-error-message-if-connect-2-.patch @@ -0,0 +1,48 @@ +From f094472efcf34cea8bf1f02a1c5c9442ffc4ca53 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 4 Feb 2021 18:02:46 +0000 +Subject: [PATCH] generator: Print a better error message if connect(2) returns + EAGAIN. + +The new error message is: + +nbd_connect_unix: connect: server backlog overflowed, see https://bugzilla.redhat.com/1925045: Resource temporarily unavailable + +Fixes: https://bugzilla.redhat.com/1925045 +Thanks: Xin Long, Lukas Doktor, Eric Blake +Reviewed-by: Martin Kletzander +(cherry picked from commit 85ed74960a658a82d7b61b0be07f43d1b2dcede9) +--- + generator/states-connect.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/generator/states-connect.c b/generator/states-connect.c +index 03b34c7..98c26e5 100644 +--- a/generator/states-connect.c ++++ b/generator/states-connect.c +@@ -70,6 +70,22 @@ STATE_MACHINE { + if (r == 0 || (r == -1 && errno == EINPROGRESS)) + return 0; + assert (r == -1); ++#ifdef __linux__ ++ if (errno == EAGAIN && family == AF_UNIX) { ++ /* This can happen on Linux when connecting to a Unix domain ++ * socket, if the server's backlog is full. Unfortunately there ++ * is nothing good we can do on the client side when this happens ++ * since any solution would involve sleeping or busy-waiting. The ++ * only solution is on the server side, increasing the backlog. ++ * But at least improve the error message. ++ * https://bugzilla.redhat.com/1925045 ++ */ ++ SET_NEXT_STATE (%.DEAD); ++ set_error (errno, "connect: server backlog overflowed, " ++ "see https://bugzilla.redhat.com/1925045"); ++ return 0; ++ } ++#endif + SET_NEXT_STATE (%.DEAD); + set_error (errno, "connect"); + return 0; +-- +2.31.1 + diff --git a/SOURCES/0004-opt_go-Tolerate-unplanned-server-death.patch b/SOURCES/0004-opt_go-Tolerate-unplanned-server-death.patch new file mode 100644 index 0000000..9080ea6 --- /dev/null +++ b/SOURCES/0004-opt_go-Tolerate-unplanned-server-death.patch @@ -0,0 +1,59 @@ +From ffe8f0a994c1f2656aa011353b386663d32db69e Mon Sep 17 00:00:00 2001 +From: Eric Blake +Date: Mon, 1 Mar 2021 15:25:31 -0600 +Subject: [PATCH] opt_go: Tolerate unplanned server death + +While debugging some experimental nbdkit code that was triggering an +assertion failure in nbdkit, I noticed a secondary failure of nbdsh +also dying from an assertion: + +libnbd: debug: nbdsh: nbd_opt_go: transition: NEWSTYLE.OPT_GO.SEND -> DEAD +libnbd: debug: nbdsh: nbd_opt_go: option queued, ignoring state machine failure +nbdsh: opt.c:86: nbd_unlocked_opt_go: Assertion `nbd_internal_is_state_negotiating (get_next_state (h))' failed. + +Although my trigger was from non-production nbdkit code, libnbd should +never die from an assertion failure merely because a server +disappeared at the wrong moment during an incomplete reply to +NBD_OPT_GO or NBD_OPT_INFO. If this is assigned a CVE, a followup +patch will add mention of it in docs/libnbd-security.pod. + +Fixes: bbf1c51392 (api: Give aio_opt_go a completion callback) +(cherry picked from commit fb4440de9cc76e9c14bd3ddf3333e78621f40ad0) +--- + lib/opt.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/lib/opt.c b/lib/opt.c +index 2317b72..e5802f4 100644 +--- a/lib/opt.c ++++ b/lib/opt.c +@@ -1,5 +1,5 @@ + /* NBD client library in userspace +- * Copyright (C) 2020 Red Hat Inc. ++ * Copyright (C) 2020-2021 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -83,7 +83,8 @@ nbd_unlocked_opt_go (struct nbd_handle *h) + + r = wait_for_option (h); + if (r == 0 && err) { +- assert (nbd_internal_is_state_negotiating (get_next_state (h))); ++ assert (nbd_internal_is_state_negotiating (get_next_state (h)) || ++ nbd_internal_is_state_dead (get_next_state (h))); + set_error (err, "server replied with error to opt_go request"); + return -1; + } +@@ -105,7 +106,8 @@ nbd_unlocked_opt_info (struct nbd_handle *h) + + r = wait_for_option (h); + if (r == 0 && err) { +- assert (nbd_internal_is_state_negotiating (get_next_state (h))); ++ assert (nbd_internal_is_state_negotiating (get_next_state (h)) || ++ nbd_internal_is_state_dead (get_next_state (h))); + set_error (err, "server replied with error to opt_info request"); + return -1; + } +-- +2.31.1 + diff --git a/SOURCES/0005-security-Document-assignment-of-CVE-2021-20286.patch b/SOURCES/0005-security-Document-assignment-of-CVE-2021-20286.patch new file mode 100644 index 0000000..8732515 --- /dev/null +++ b/SOURCES/0005-security-Document-assignment-of-CVE-2021-20286.patch @@ -0,0 +1,40 @@ +From 171ffdde8be590f784086a021a7e6f36c4ecdb4b Mon Sep 17 00:00:00 2001 +From: Eric Blake +Date: Fri, 12 Mar 2021 17:00:58 -0600 +Subject: [PATCH] security: Document assignment of CVE-2021-20286 + +Now that we finally have a CVE number, it's time to document +the problem (it's low severity, but still a denial of service). + +Fixes: fb4440de9cc7 (opt_go: Tolerate unplanned server death) +(cherry picked from commit 40308a005eaa6b2e8f98da8952d0c0cacc51efde) +--- + docs/libnbd-security.pod | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/docs/libnbd-security.pod b/docs/libnbd-security.pod +index d8ead87..0cae846 100644 +--- a/docs/libnbd-security.pod ++++ b/docs/libnbd-security.pod +@@ -22,6 +22,12 @@ L + See the full announcement here: + L + ++=head2 CVE-2021-20286 ++denial of service when using L ++ ++See the full announcement here: ++L ++ + =head1 SEE ALSO + + L. +@@ -34,4 +40,4 @@ Richard W.M. Jones + + =head1 COPYRIGHT + +-Copyright (C) 2019 Red Hat Inc. ++Copyright (C) 2019-2021 Red Hat Inc. +-- +2.31.1 + diff --git a/SOURCES/0006-copy-Pass-in-dummy-variable-rather-than-errno-to-cal.patch b/SOURCES/0006-copy-Pass-in-dummy-variable-rather-than-errno-to-cal.patch new file mode 100644 index 0000000..896876f --- /dev/null +++ b/SOURCES/0006-copy-Pass-in-dummy-variable-rather-than-errno-to-cal.patch @@ -0,0 +1,163 @@ +From 22572f8ac13e2e8daf91d227eac2f384303fb5b4 Mon Sep 17 00:00:00 2001 +From: Eric Blake +Date: Thu, 3 Feb 2022 14:25:57 -0600 +Subject: [PATCH] copy: Pass in dummy variable rather than &errno to callback + +In several places where asynch handlers manually call the provided +nbd_completion_callback, the value of errno is indeterminate (for +example, in file-ops.c:file_asynch_read(), the previous call to +file_synch_read() already triggered exit() on error, but does not +guarantee what is left in errno on success). As the callback should +be paying attention to the value of *error (to be fixed in the next +patch), we are better off ensuring that we pass in a pointer to a +known-zero value. Besides, passing in &errno carries a risk that if +the callback uses any other library function that alters errno prior +to dereferncing *error, it will no longer see the value we passed in. +Thus, it is easier to use a dummy variable on the stack than to mess +around with errno and it's magic macro expansion into a thread-local +storage location. + +Note that several callsites then check if the callback returned -1, +and if so assume that the callback has caused errno to now have a sane +value to pass on to perror. In theory, the fact that we are no longer +passing in &errno means that if the callback assigns into *error but +did not otherwise affect errno (a tenuous assumption, given our +argument above that we could not even guarantee that the callback does +not accidentally alter errno prior to reading *error), our perror call +would no longer reflect the intended error value from the callback. +But in practice, since the callback never actually returned -1, nor +even assigned into *error, the call to perror is dead code; although I +have chosen to defer that additional cleanup to the next patch. + +Message-Id: <20220203202558.203013-5-eblake@redhat.com> +Acked-by: Richard W.M. Jones +Acked-by: Nir Soffer +Reviewed-by: Laszlo Ersek +(cherry picked from commit 794c8ce06e995ebd282e8f2b9465a06140572112) +Conflicts: + copy/file-ops.c - no backport of d5f65e56 ("copy: Do not use trim + for zeroing"), so asynch_trim needed same treatment + copy/multi-thread-copying.c - context due to missing refactoring + copy/null-ops.c - no backport of 0b16205e "copy: Implement "null:" + destination." +(cherry picked from commit 26e3dcf80815fe2db320d3046aabc2580c2f7a0d) +--- + copy/file-ops.c | 22 +++++++++++++--------- + copy/multi-thread-copying.c | 8 +++++--- + 2 files changed, 18 insertions(+), 12 deletions(-) + +diff --git a/copy/file-ops.c b/copy/file-ops.c +index 086348a..cc312b4 100644 +--- a/copy/file-ops.c ++++ b/copy/file-ops.c +@@ -1,5 +1,5 @@ + /* NBD client library in userspace. +- * Copyright (C) 2020 Red Hat Inc. ++ * Copyright (C) 2020-2022 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -158,10 +158,11 @@ file_asynch_read (struct rw *rw, + struct command *command, + nbd_completion_callback cb) + { ++ int dummy = 0; ++ + file_synch_read (rw, slice_ptr (command->slice), + command->slice.len, command->offset); +- errno = 0; +- if (cb.callback (cb.user_data, &errno) == -1) { ++ if (cb.callback (cb.user_data, &dummy) == -1) { + perror (rw->name); + exit (EXIT_FAILURE); + } +@@ -172,10 +173,11 @@ file_asynch_write (struct rw *rw, + struct command *command, + nbd_completion_callback cb) + { ++ int dummy = 0; ++ + file_synch_write (rw, slice_ptr (command->slice), + command->slice.len, command->offset); +- errno = 0; +- if (cb.callback (cb.user_data, &errno) == -1) { ++ if (cb.callback (cb.user_data, &dummy) == -1) { + perror (rw->name); + exit (EXIT_FAILURE); + } +@@ -185,10 +187,11 @@ static bool + file_asynch_trim (struct rw *rw, struct command *command, + nbd_completion_callback cb) + { ++ int dummy = 0; ++ + if (!file_synch_trim (rw, command->offset, command->slice.len)) + return false; +- errno = 0; +- if (cb.callback (cb.user_data, &errno) == -1) { ++ if (cb.callback (cb.user_data, &dummy) == -1) { + perror (rw->name); + exit (EXIT_FAILURE); + } +@@ -199,10 +202,11 @@ static bool + file_asynch_zero (struct rw *rw, struct command *command, + nbd_completion_callback cb) + { ++ int dummy = 0; ++ + if (!file_synch_zero (rw, command->offset, command->slice.len)) + return false; +- errno = 0; +- if (cb.callback (cb.user_data, &errno) == -1) { ++ if (cb.callback (cb.user_data, &dummy) == -1) { + perror (rw->name); + exit (EXIT_FAILURE); + } +diff --git a/copy/multi-thread-copying.c b/copy/multi-thread-copying.c +index a7aaa7d..2593ff7 100644 +--- a/copy/multi-thread-copying.c ++++ b/copy/multi-thread-copying.c +@@ -1,5 +1,5 @@ + /* NBD client library in userspace. +- * Copyright (C) 2020 Red Hat Inc. ++ * Copyright (C) 2020-2022 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -391,6 +391,7 @@ finished_read (void *vp, int *error) + bool last_is_hole = false; + uint64_t i; + struct command *newcommand; ++ int dummy = 0; + + /* Iterate over whole blocks in the command, starting on a block + * boundary. +@@ -473,7 +474,7 @@ finished_read (void *vp, int *error) + /* Free the original command since it has been split into + * subcommands and the original is no longer needed. + */ +- free_command (command, &errno); ++ free_command (command, &dummy); + } + + return 1; /* auto-retires the command */ +@@ -498,6 +499,7 @@ static void + fill_dst_range_with_zeroes (struct command *command) + { + char *data; ++ int dummy = 0; + + if (destination_is_zero) + goto free_and_return; +@@ -541,7 +543,7 @@ fill_dst_range_with_zeroes (struct command *command) + free (data); + + free_and_return: +- free_command (command, &errno); ++ free_command (command, &dummy); + } + + static int +-- +2.31.1 + diff --git a/SOURCES/0007-copy-CVE-2022-0485-Fail-nbdcopy-if-NBD-read-or-write.patch b/SOURCES/0007-copy-CVE-2022-0485-Fail-nbdcopy-if-NBD-read-or-write.patch new file mode 100644 index 0000000..b191e8b --- /dev/null +++ b/SOURCES/0007-copy-CVE-2022-0485-Fail-nbdcopy-if-NBD-read-or-write.patch @@ -0,0 +1,318 @@ +From 1b0b732e6a9b4979fccf6a09eb6704264edf675d Mon Sep 17 00:00:00 2001 +From: Eric Blake +Date: Thu, 3 Feb 2022 14:25:58 -0600 +Subject: [PATCH] copy: CVE-2022-0485: Fail nbdcopy if NBD read or write fails + +nbdcopy has a nasty bug when performing multi-threaded copies using +asynchronous nbd calls - it was blindly treating the completion of an +asynchronous command as successful, rather than checking the *error +parameter. This can result in the silent creation of a corrupted +image in two different ways: when a read fails, we blindly wrote +garbage to the destination; when a write fails, we did not flag that +the destination was not written. + +Since nbdcopy already calls exit() on a synchronous read or write +failure to a file, doing the same for an asynchronous op to an NBD +server is the simplest solution. A nicer solution, but more invasive +to code and thus not done here, might be to allow up to N retries of +the transaction (in case the read or write failure was transient), or +even having a mode where as much data is copied as possible (portions +of the copy that failed would be logged on stderr, and nbdcopy would +still fail with a non-zero exit status, but this would copy more than +just stopping at the first error, as can be done with rsync or +ddrescue). + +Note that since we rely on auto-retiring and do NOT call +nbd_aio_command_completed, our completion callbacks must always return +1 (if they do not exit() first), even when acting on *error, so as not +leave the command allocated until nbd_close. As such, there is no +sane way to return an error to a manual caller of the callback, and +therefore we can drop dead code that calls perror() and exit() if the +callback "failed". It is also worth documenting the contract on when +we must manually call the callback during the asynch_zero callback, so +that we do not leak or double-free the command; thankfully, all the +existing code paths were correct. + +The added testsuite script demonstrates several scenarios, some of +which fail without the rest of this patch in place, and others which +showcase ways in which sparse images can bypass errors. + +Once backports are complete, a followup patch on the main branch will +edit docs/libnbd-security.pod with the mailing list announcement of +the stable branch commit ids and release versions that incorporate +this fix. + +Reported-by: Nir Soffer +Fixes: bc896eec4d ("copy: Implement multi-conn, multiple threads, multiple requests in flight.", v1.5.6) +Fixes: https://bugzilla.redhat.com/2046194 +Message-Id: <20220203202558.203013-6-eblake@redhat.com> +Acked-by: Richard W.M. Jones +Acked-by: Nir Soffer +[eblake: fix error message per Nir, tweak requires lines in unit test per Rich] +Reviewed-by: Laszlo Ersek + +(cherry picked from commit 8d444b41d09a700c7ee6f9182a649f3f2d325abb) +Conflicts: + copy/nbdcopy.h - copyright context + copy/null-ops.c - no backport of 0b16205e "copy: Implement "null:" + destination." + copy/copy-nbd-error.sh - no backport of d5f65e56 ("copy: Do not use + trim for zeroing"), so one test needed an additional error-trim-rate; + no backport of 4ff9e62d (copy: Add --request-size option") and friends, so + this version uses larger transactions, so change error rate of 0.5 to 1; + no backport of 0b16205e "copy: Implement "null:" destination.", so use + nbdkit null instead +Note that while the use of NBD_CMD_TRIM can create data corruption, it is +not as severe as what this patch fixes, since trim corruption will only +expose what had previously been on the disk, compared to this patch fixing +a potential leak of nbdcopy heap contents into the destination. +(cherry picked from commit 6c8f2f859926b82094fb5e85c446ea099700fa10) +--- + TODO | 1 + + copy/Makefile.am | 4 +- + copy/copy-nbd-error.sh | 81 +++++++++++++++++++++++++++++++++++++ + copy/file-ops.c | 17 +++----- + copy/multi-thread-copying.c | 13 ++++++ + copy/nbdcopy.h | 7 ++-- + 6 files changed, 107 insertions(+), 16 deletions(-) + create mode 100755 copy/copy-nbd-error.sh + +diff --git a/TODO b/TODO +index 510c219..19c21d4 100644 +--- a/TODO ++++ b/TODO +@@ -35,6 +35,7 @@ nbdcopy: + - Better page cache usage, see nbdkit-file-plugin options + fadvise=sequential cache=none. + - Consider io_uring if there are performance bottlenecks. ++ - Configurable retries in response to read or write failures. + + nbdfuse: + - If you write beyond the end of the virtual file, it returns EIO. +diff --git a/copy/Makefile.am b/copy/Makefile.am +index d318388..3406cd8 100644 +--- a/copy/Makefile.am ++++ b/copy/Makefile.am +@@ -1,5 +1,5 @@ + # nbd client library in userspace +-# Copyright (C) 2020 Red Hat Inc. ++# Copyright (C) 2020-2022 Red Hat Inc. + # + # This library is free software; you can redistribute it and/or + # modify it under the terms of the GNU Lesser General Public +@@ -30,6 +30,7 @@ EXTRA_DIST = \ + copy-nbd-to-small-nbd-error.sh \ + copy-nbd-to-sparse-file.sh \ + copy-nbd-to-stdout.sh \ ++ copy-nbd-error.sh \ + copy-progress-bar.sh \ + copy-sparse.sh \ + copy-sparse-allocated.sh \ +@@ -105,6 +106,7 @@ TESTS += \ + copy-nbd-to-sparse-file.sh \ + copy-stdin-to-nbd.sh \ + copy-nbd-to-stdout.sh \ ++ copy-nbd-error.sh \ + copy-progress-bar.sh \ + copy-sparse.sh \ + copy-sparse-allocated.sh \ +diff --git a/copy/copy-nbd-error.sh b/copy/copy-nbd-error.sh +new file mode 100755 +index 0000000..bba71db +--- /dev/null ++++ b/copy/copy-nbd-error.sh +@@ -0,0 +1,81 @@ ++#!/usr/bin/env bash ++# nbd client library in userspace ++# Copyright (C) 2022 Red Hat Inc. ++# ++# This library is free software; you can redistribute it and/or ++# modify it under the terms of the GNU Lesser General Public ++# License as published by the Free Software Foundation; either ++# version 2 of the License, or (at your option) any later version. ++# ++# This library is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++# Lesser General Public License for more details. ++# ++# You should have received a copy of the GNU Lesser General Public ++# License along with this library; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ ++# Tests several scenarios of handling NBD server errors ++# Serves as a regression test for the CVE-2022-0485 fix. ++ ++. ../tests/functions.sh ++ ++set -e ++set -x ++ ++requires nbdkit --exit-with-parent --version ++requires nbdkit --filter=noextents null --version ++requires nbdkit --filter=error pattern --version ++requires nbdkit --filter=nozero memory --version ++ ++fail=0 ++ ++# Failure to get block status should not be fatal, but merely downgrade to ++# reading the entire image as if data ++echo "Testing extents failures on source" ++$VG nbdcopy -- [ nbdkit --exit-with-parent -v --filter=error pattern 5M \ ++ error-extents-rate=1 ] [ nbdkit --exit-with-parent -v null 5M ] || fail=1 ++ ++# Failure to read should be fatal ++echo "Testing read failures on non-sparse source" ++$VG nbdcopy -- [ nbdkit --exit-with-parent -v --filter=error pattern 5M \ ++ error-pread-rate=1 ] [ nbdkit --exit-with-parent -v null 5M ] && fail=1 ++ ++# However, reliable block status on a sparse image can avoid the need to read ++echo "Testing read failures on sparse source" ++$VG nbdcopy -- [ nbdkit --exit-with-parent -v --filter=error null 5M \ ++ error-pread-rate=1 ] [ nbdkit --exit-with-parent -v null 5M ] || fail=1 ++ ++# Failure to write data should be fatal ++echo "Testing write data failures on arbitrary destination" ++$VG nbdcopy -- [ nbdkit --exit-with-parent -v pattern 5M ] \ ++ [ nbdkit --exit-with-parent -v --filter=error --filter=noextents \ ++ memory 5M error-pwrite-rate=1 ] && fail=1 ++ ++# However, writing zeroes can bypass the need for normal writes ++echo "Testing write data failures from sparse source" ++$VG nbdcopy -- [ nbdkit --exit-with-parent -v null 5M ] \ ++ [ nbdkit --exit-with-parent -v --filter=error --filter=noextents \ ++ memory 5M error-pwrite-rate=1 ] || fail=1 ++ ++# Failure to write zeroes should be fatal ++echo "Testing write zero failures on arbitrary destination" ++$VG nbdcopy -- [ nbdkit --exit-with-parent -v null 5M ] \ ++ [ nbdkit --exit-with-parent -v --filter=error memory 5M \ ++ error-trim-rate=1 error-zero-rate=1 ] && fail=1 ++ ++# However, assuming/learning destination is zero can skip need to write ++echo "Testing write failures on pre-zeroed destination" ++$VG nbdcopy --destination-is-zero -- \ ++ [ nbdkit --exit-with-parent -v null 5M ] \ ++ [ nbdkit --exit-with-parent -v --filter=error memory 5M \ ++ error-pwrite-rate=1 error-zero-rate=1 ] || fail=1 ++ ++# Likewise, when write zero is not advertised, fallback to normal write works ++echo "Testing write zeroes to destination without zero support" ++$VG nbdcopy -- [ nbdkit --exit-with-parent -v null 5M ] \ ++ [ nbdkit --exit-with-parent -v --filter=nozero --filter=error memory 5M \ ++ error-zero-rate=1 ] || fail=1 ++ ++exit $fail +diff --git a/copy/file-ops.c b/copy/file-ops.c +index cc312b4..b19af04 100644 +--- a/copy/file-ops.c ++++ b/copy/file-ops.c +@@ -162,10 +162,8 @@ file_asynch_read (struct rw *rw, + + file_synch_read (rw, slice_ptr (command->slice), + command->slice.len, command->offset); +- if (cb.callback (cb.user_data, &dummy) == -1) { +- perror (rw->name); +- exit (EXIT_FAILURE); +- } ++ /* file_synch_read called exit() on error */ ++ cb.callback (cb.user_data, &dummy); + } + + static void +@@ -177,10 +175,8 @@ file_asynch_write (struct rw *rw, + + file_synch_write (rw, slice_ptr (command->slice), + command->slice.len, command->offset); +- if (cb.callback (cb.user_data, &dummy) == -1) { +- perror (rw->name); +- exit (EXIT_FAILURE); +- } ++ /* file_synch_write called exit() on error */ ++ cb.callback (cb.user_data, &dummy); + } + + static bool +@@ -206,10 +202,7 @@ file_asynch_zero (struct rw *rw, struct command *command, + + if (!file_synch_zero (rw, command->offset, command->slice.len)) + return false; +- if (cb.callback (cb.user_data, &dummy) == -1) { +- perror (rw->name); +- exit (EXIT_FAILURE); +- } ++ cb.callback (cb.user_data, &dummy); + return true; + } + +diff --git a/copy/multi-thread-copying.c b/copy/multi-thread-copying.c +index 2593ff7..28749ae 100644 +--- a/copy/multi-thread-copying.c ++++ b/copy/multi-thread-copying.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + + #include + +@@ -374,6 +375,12 @@ finished_read (void *vp, int *error) + { + struct command *command = vp; + ++ if (*error) { ++ fprintf (stderr, "read at offset %" PRId64 " failed: %s\n", ++ command->offset, strerror (*error)); ++ exit (EXIT_FAILURE); ++ } ++ + if (allocated || sparse_size == 0) { + /* If sparseness detection (see below) is turned off then we write + * the whole command. +@@ -552,6 +559,12 @@ free_command (void *vp, int *error) + struct command *command = vp; + struct buffer *buffer = command->slice.buffer; + ++ if (*error) { ++ fprintf (stderr, "write at offset %" PRId64 " failed: %s\n", ++ command->offset, strerror (*error)); ++ exit (EXIT_FAILURE); ++ } ++ + if (buffer != NULL) { + if (--buffer->refs == 0) { + free (buffer->data); +diff --git a/copy/nbdcopy.h b/copy/nbdcopy.h +index 3dcc6df..9626a52 100644 +--- a/copy/nbdcopy.h ++++ b/copy/nbdcopy.h +@@ -1,5 +1,5 @@ + /* NBD client library in userspace. +- * Copyright (C) 2020 Red Hat Inc. ++ * Copyright (C) 2020-2022 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -134,7 +134,8 @@ struct rw_ops { + bool (*synch_zero) (struct rw *rw, uint64_t offset, uint64_t count); + + /* Asynchronous I/O operations. These start the operation and call +- * 'cb' on completion. ++ * 'cb' on completion. 'cb' will return 1, for auto-retiring with ++ * asynchronous libnbd calls. + * + * The file_ops versions are actually implemented synchronously, but + * still call 'cb'. +@@ -156,7 +157,7 @@ struct rw_ops { + nbd_completion_callback cb); + + /* Asynchronously zero. command->slice.buffer is not used. If not possible, +- * returns false. ++ * returns false. 'cb' must be called only if returning true. + */ + bool (*asynch_zero) (struct rw *rw, struct command *command, + nbd_completion_callback cb); +-- +2.31.1 + diff --git a/SOURCES/copy-patches.sh b/SOURCES/copy-patches.sh new file mode 100755 index 0000000..e00af4e --- /dev/null +++ b/SOURCES/copy-patches.sh @@ -0,0 +1,55 @@ +#!/bin/bash - + +set -e + +# Maintainer script to copy patches from the git repo to the current +# directory. Use it like this: +# ./copy-patches.sh + +rhel_version=8.6 + +# Check we're in the right directory. +if [ ! -f libnbd.spec ]; then + echo "$0: run this from the directory containing 'libnbd.spec'" + exit 1 +fi + +git_checkout=$HOME/d/libnbd-rhel-$rhel_version +if [ ! -d $git_checkout ]; then + echo "$0: $git_checkout does not exist" + echo "This script is only for use by the maintainer when preparing a" + echo "libnbd release on RHEL." + exit 1 +fi + +# Get the base version of libnbd. +version=`grep '^Version:' libnbd.spec | awk '{print $2}'` +tag="v$version" + +# Remove any existing patches. +git rm -f [0-9]*.patch ||: +rm -f [0-9]*.patch + +# Get the patches. +(cd $git_checkout; rm -f [0-9]*.patch; git format-patch -N $tag) +mv $git_checkout/[0-9]*.patch . + +# Remove any not to be applied. +rm -f *NOT-FOR-RPM*.patch + +# Add the patches. +git add [0-9]*.patch + +# Print out the patch lines. +echo +echo "--- Copy the following text into libnbd.spec file" +echo + +echo "# Patches." +for f in [0-9]*.patch; do + n=`echo $f | awk -F- '{print $1}'` + echo "Patch$n: $f" +done + +echo +echo "--- End of text" diff --git a/SOURCES/libnbd-1.2.2.tar.gz.sig b/SOURCES/libnbd-1.2.2.tar.gz.sig deleted file mode 100644 index a353ae2..0000000 --- a/SOURCES/libnbd-1.2.2.tar.gz.sig +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQJFBAABCAAvFiEE93dPsa0HSn6Mh2fqkXOPc+G3aKAFAl46yJwRHHJpY2hAYW5u -ZXhpYS5vcmcACgkQkXOPc+G3aKBNsBAAi+GGHQZ/2VZ/LiqO+kc5hzAsbxfCUb6e -epLTmHry0sFdDFGeVwaYZxvnALu61nybMvaHsASW8FlWDbtgaqx6tUApgeV46oXb -yvT1EzmxR4+U8cwb9IEDRBjB1WWqYv+TYltljkV5nmO1DADOZr9AV5yRp872SWsP -oshiuuxwIw+ugQfXkyhUPgyO2FzjxntmWCjD13nQhDeD42UQUE+0mxAgXiGfsOj4 -eArCsa4Us/ZWpg9QkoXomzpN1xzMoTEQ9a+7PVTn54bfCCgThdodVN5rNxuNhSKU -yYHl6hUW1gnZaQ17FfqZrOcEFjvVnDYDlkoHF9Vp4AOTNMCd/w008NjKt+yR90WW -yeiJ21mRpkA/6pK5irHEBMFmk9cf/5IIhgF9Xtl5erqKYQu6W5NqXr4BCTdDS4BL -auHV+I8Dqi9IEH8bhZYxErS/eba6N6ZJWkwHqbdwtfZBBSqEcZXrTXviXw0BpCsy -lPGx2xXXmGPB5/BojOmFOEzfPMtiLsLrVW5DwlBoeT90kFEhi7+bGB5gJ24NxL1y -/2XcTxRhv79FMBdLx+6O7/js/tMrOxmKRkUcInU4rjOg+VGmwx6+LeJ2P0au8ufx -8Rvk7DHUF0ZazsbJ4IkL0XP4jWIgB2xMJqjIq+E2duLO+c1sDUBxoU43BgCLYUV9 -92Ap8fwsbvA= -=zo1m ------END PGP SIGNATURE----- diff --git a/SOURCES/libnbd-1.6.0.tar.gz.sig b/SOURCES/libnbd-1.6.0.tar.gz.sig new file mode 100644 index 0000000..fa6006d --- /dev/null +++ b/SOURCES/libnbd-1.6.0.tar.gz.sig @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJFBAABCAAvFiEE93dPsa0HSn6Mh2fqkXOPc+G3aKAFAl/3RFQRHHJpY2hAYW5u +ZXhpYS5vcmcACgkQkXOPc+G3aKD9aw/+Pfg3owjJmhTcCyFvuH2lgiiBb+qL2An+ +hsoax6dM5JxzV6x1Ikgn3C8z2+dLRMowo2FrRgpzTwfaS+ngLDipSC04hKl9MhFN +7OPLCm+L7wcP7KUk4cC0qTSHpHkApo2SP3/bD7vVBYZMYSjgUVFcRoqZlRl3N9RF +7XNsxA2YG9bV4Ln3KbB+k2uxIKNUZIVjmEpretVbb+NTKW9C23ZHicSHYB+Eok1M +iTN6j66rYFn0Xb+L2v7jty19tSdYOMbkdSn0KpniURAWevjjVWGqcojMqW4YuAZ5 +h2MpRfyKFyusbsbtX5bjICTu6+AgFFUALKH7ReDs1RY1cEph9XdBLVulXTggxY05 +E3I1Nns1YmjRlV6ky2Abl2e+Doc44mycINRlwL2q8+Q3TqlVVPFXoVTWxIJ6/Uae +tqnEwWIa2wGv3KU1KLNbWTn1z6I8NM/Nj+7pMKDNnxJzFmHEjL94tmG+iNmHsF34 +vWBZ1q7h9EezxHLOPFYDjlpS+IxeuXakbpuTX2jXvi3zSAbr5WmRR1uO8dAiwu9b +RwOHRmVQOFLAAICYTZDmxl42DpWs5Z2aP7eRwpe8/MOSRiAVepjhUD/bsdaFwmBR +8Z7CGNzyTtt+sy5l7cPBYZ+4RdxWgFEBceBbHs06zdlD/Pui288UQVB/0e9AXYOc +wluyWT1v7sA= +=BaN1 +-----END PGP SIGNATURE----- diff --git a/SPECS/libnbd.spec b/SPECS/libnbd.spec index 0f8acb9..0020064 100644 --- a/SPECS/libnbd.spec +++ b/SPECS/libnbd.spec @@ -2,14 +2,14 @@ %global verify_tarball_signature 1 # If there are patches which touch autotools files, set this to 1. -%global patches_touch_autotools %{nil} +%global patches_touch_autotools 1 # The source directory. -%global source_directory 1.2-stable +%global source_directory 1.6-stable Name: libnbd -Version: 1.2.2 -Release: 1%{?dist} +Version: 1.6.0 +Release: 5%{?dist} Summary: NBD client library in userspace License: LGPLv2+ @@ -22,6 +22,21 @@ Source1: http://libguestfs.org/download/libnbd/%{source_directory}/%{name # https://pgp.key-server.io/pks/lookup?search=rjones%40redhat.com&fingerprint=on&op=vindex Source2: libguestfs.keyring +# Maintainer script which helps with handling patches. +Source3: copy-patches.sh + +# Patches come from this upstream branch: +# https://github.com/libguestfs/libnbd/tree/rhel-8.6 + +# Patches. +Patch0001: 0001-copy-copy-nbd-to-sparse-file.sh-Skip-test-unless-nbd.patch +Patch0002: 0002-generator-Refactor-CONNECT.START-state.patch +Patch0003: 0003-generator-Print-a-better-error-message-if-connect-2-.patch +Patch0004: 0004-opt_go-Tolerate-unplanned-server-death.patch +Patch0005: 0005-security-Document-assignment-of-CVE-2021-20286.patch +Patch0006: 0006-copy-Pass-in-dummy-variable-rather-than-errno-to-cal.patch +Patch0007: 0007-copy-CVE-2022-0485-Fail-nbdcopy-if-NBD-read-or-write.patch + %if 0%{patches_touch_autotools} BuildRequires: autoconf, automake, libtool %endif @@ -45,21 +60,32 @@ BuildRequires: python3-devel # For the OCaml bindings. BuildRequires: ocaml BuildRequires: ocaml-findlib-devel +BuildRequires: ocaml-ocamldoc # Only for building the examples. BuildRequires: glib2-devel +# For bash-completion. +BuildRequires: bash-completion + # Only for running the test suite. +BuildRequires: coreutils +BuildRequires: gcc-c++ BuildRequires: gnutls-utils +#BuildRequires: jq %ifnarch %{ix86} BuildRequires: nbdkit -#BuildRequires: nbdkit-memory-plugin -#BuildRequires: nbdkit-null-plugin -#BuildRequires: nbdkit-pattern-plugin -#BuildRequires: nbdkit-sh-plugin +BuildRequires: nbdkit-data-plugin +#BuildRequires: nbdkit-eval-plugin +BuildRequires: nbdkit-memory-plugin +BuildRequires: nbdkit-null-plugin +BuildRequires: nbdkit-pattern-plugin +BuildRequires: nbdkit-sh-plugin +#BuildRequires: nbdkit-sparse-random-plugin +#BuildRequires: nbd BuildRequires: qemu-img %endif -BuildRequires: gcc-c++ +BuildRequires: util-linux %description @@ -137,6 +163,20 @@ Requires: %{name}%{?_isa} = %{version}-%{release} This package contains FUSE support for %{name}. +%package bash-completion +Summary: Bash tab-completion for %{name} +BuildArch: noarch +Requires: bash-completion >= 2.0 +# Don't use _isa here because it's a noarch package. This dependency +# is just to ensure that the subpackage is updated along with libnbd. +Requires: %{name} = %{version}-%{release} + + +%description bash-completion +Install this package if you want intelligent bash tab-completion +for %{name}. + + %prep %if 0%{verify_tarball_signature} tmphome="$(mktemp -d)" @@ -155,7 +195,8 @@ autoreconf -i PYTHON=%{__python3} \ --enable-python \ --enable-ocaml \ - --enable-fuse + --enable-fuse \ + --disable-golang make %{?_smp_mflags} @@ -166,6 +207,9 @@ make %{?_smp_mflags} # Delete libtool crap. find $RPM_BUILD_ROOT -name '*.la' -delete +# Delete the golang man page since we're not distributing the bindings. +rm $RPM_BUILD_ROOT%{_mandir}/man3/libnbd-golang.3* + %check # interop/structured-read.sh fails with the old qemu-nbd in Fedora 29, @@ -185,6 +229,12 @@ for f in fuse/test-*.sh; do chmod +x $f done +# info/info-map-base-allocation-json.sh fails because of a bug in +# jq 1.5 in RHEL 8 (fixed in later versions). +rm info/info-map-base-allocation-json.sh +touch info/info-map-base-allocation-json.sh +chmod +x info/info-map-base-allocation-json.sh + make %{?_smp_mflags} check || { for f in $(find -name test-suite.log); do echo @@ -198,7 +248,11 @@ make %{?_smp_mflags} check || { %files %doc README %license COPYING.LIB +%{_bindir}/nbdcopy +%{_bindir}/nbdinfo %{_libdir}/libnbd.so.* +%{_mandir}/man1/nbdcopy.1* +%{_mandir}/man1/nbdinfo.1* %files devel @@ -231,6 +285,8 @@ make %{?_smp_mflags} check || { %{_libdir}/ocaml/nbd/*.cmx %{_libdir}/ocaml/nbd/*.mli %{_mandir}/man3/libnbd-ocaml.3* +%{_mandir}/man3/NBD.3* +%{_mandir}/man3/NBD.*.3* %files -n python3-%{name} @@ -247,7 +303,23 @@ make %{?_smp_mflags} check || { %{_mandir}/man1/nbdfuse.1* +%files bash-completion +%dir %{_datadir}/bash-completion/completions +%{_datadir}/bash-completion/completions/nbdcopy +%{_datadir}/bash-completion/completions/nbdfuse +%{_datadir}/bash-completion/completions/nbdinfo +%{_datadir}/bash-completion/completions/nbdsh + + %changelog +* Mon Feb 7 2022 Richard W.M. Jones - 1.6.0-5.el8 +- Fix CVE-2022-0485: Fail nbdcopy if NBD read or write fails + resolves: rhbz#2045718 + +* Thu Sep 2 2021 Danilo C. L. de Paula - 1.6.0-4.el8 +- Resolves: bz#2000225 + (Rebase virt:rhel module:stream based on AV-8.6) + * Mon Jul 13 2020 Danilo C. L. de Paula - 1.2.2 - Resolves: bz#1844296 (Upgrade components in virt:rhel module:stream for RHEL-8.3 release)