Blame SOURCES/0001-api-Add-new-API-nbd_set_pread_initialize.patch

fb811a
From c79706af4e7475bf58861a143b77b77a54e7a1cd Mon Sep 17 00:00:00 2001
fb811a
From: Eric Blake <eblake@redhat.com>
fb811a
Date: Wed, 9 Feb 2022 15:39:49 -0600
fb811a
Subject: [PATCH] api: Add new API nbd_set_pread_initialize()
fb811a
fb811a
The recent patch series for CVE-2022-0485 demonstrated that when
fb811a
applications using libnbd are not careful about error checking, the
fb811a
difference on whether a data leak is at least sanitized (all zeroes,
fb811a
partial reads, or data leftover from a prior read) vs. a dangerous
fb811a
information leak (uninitialized data from the heap) was partly under
fb811a
libnbd's control.  The previous two patches changed libnbd to always
fb811a
sanitize, as a security hardening technique that prevents heap leaks
fb811a
no matter how buggy the client app is.  But a blind memset() also adds
fb811a
an execution delay, even if it doesn't show up as the hot spot in our
fb811a
profiling when compared to the time spent with network traffic.
fb811a
fb811a
At any rate, if client apps choose to pre-initialize their buffers, or
fb811a
otherwise audit their code to take on their own risk about not
fb811a
dereferencing a buffer on failure paths, then the time spent by libnbd
fb811a
doing memset() is wasted; so it is worth adding a knob to let a user
fb811a
opt in to faster execution at the expense of giving up our memset()
fb811a
hardening on their behalf.
fb811a
fb811a
In addition to adding two new APIs, this patch also causes changes to
fb811a
the four existing APIs nbd_{aio_,}pread{,_structured}, with those
fb811a
generated lib/api.c changes looking like:
fb811a
fb811a
| --- lib/api.c.bak	2022-02-10 08:17:09.973381979 -0600
fb811a
| +++ lib/api.c	2022-02-10 08:22:27.503428024 -0600
fb811a
| @@ -2871,7 +2914,8 @@ nbd_pread (struct nbd_handle *h, void *b
fb811a
|      debug (h, "enter: buf=<buf> count=%zu offset=%" PRIu64 " flags=0x%x", count, offset, flags);
fb811a
|    }
fb811a
|
fb811a
| -  memset (buf, 0, count);
fb811a
| +  if (h->pread_initialize)
fb811a
| +    memset (buf, 0, count);
fb811a
|    if (unlikely (!pread_in_permitted_state (h))) {
fb811a
|      ret = -1;
fb811a
|      goto out;
fb811a
fb811a
Message-Id: <20220209220726.1902761-4-eblake@redhat.com>
fb811a
Acked-by: Laszlo Ersek <lersek@redhat.com>
fb811a
[eblake: enhance commit message to show generated file diff, mention CVE
fb811a
in doc text]
fb811a
Reviewed-by: Richard W.M. Jones <rjones@redhat.com>
fb811a
fb811a
(cherry picked from commit e0953cb71250947bb97b25e34ff1ea34bd504bf3)
fb811a
---
fb811a
 generator/API.ml                              | 90 ++++++++++++++++---
fb811a
 generator/C.ml                                |  3 +-
fb811a
 .../libnbd/libnbd_110_defaults_test.go        | 10 ++-
fb811a
 .../libnbd_120_set_non_defaults_test.go       | 12 +++
fb811a
 lib/handle.c                                  | 17 +++-
fb811a
 lib/internal.h                                |  5 +-
fb811a
 ocaml/tests/test_110_defaults.ml              |  4 +-
fb811a
 ocaml/tests/test_120_set_non_defaults.ml      |  5 +-
fb811a
 python/t/110-defaults.py                      |  3 +-
fb811a
 python/t/120-set-non-defaults.py              |  4 +-
fb811a
 tests/errors.c                                | 25 +++++-
fb811a
 11 files changed, 156 insertions(+), 22 deletions(-)
fb811a
fb811a
diff --git a/generator/API.ml b/generator/API.ml
fb811a
index d8df7c8..00ab34f 100644
fb811a
--- a/generator/API.ml
fb811a
+++ b/generator/API.ml
fb811a
@@ -778,6 +778,49 @@ the time of compilation.";
fb811a
                 Link "aio_is_created"; Link "aio_is_ready"];
fb811a
   };
fb811a
 
fb811a
+  "set_pread_initialize", {
fb811a
+    default_call with
fb811a
+    args = [Bool "request"]; ret = RErr;
fb811a
+    shortdesc = "control whether libnbd pre-initializes read buffers";
fb811a
+    longdesc = "\
fb811a
+By default, libnbd will pre-initialize the contents of a buffer
fb811a
+passed to calls such as L<nbd_pread(3)> to all zeroes prior to
fb811a
+checking for any other errors, so that even if a client application
fb811a
+passed in an uninitialized buffer but fails to check for errors, it
fb811a
+will not result in a potential security risk caused by an accidental
fb811a
+leak of prior heap contents (see CVE-2022-0485 in
fb811a
+L<libnbd-security(3)> for an example of a security hole in an
fb811a
+application built against an earlier version of libnbd that lacked
fb811a
+consistent pre-initialization).  However, for a client application
fb811a
+that has audited that an uninitialized buffer is never dereferenced,
fb811a
+or which performs its own pre-initialization, libnbd's sanitization
fb811a
+efforts merely pessimize performance (although the time spent in
fb811a
+pre-initialization may pale in comparison to time spent waiting on
fb811a
+network packets).
fb811a
+
fb811a
+Calling this function with C<request> set to false tells libnbd to
fb811a
+skip the buffer initialization step in read commands.";
fb811a
+    see_also = [Link "get_pread_initialize";
fb811a
+                Link "set_strict_mode";
fb811a
+                Link "pread"; Link "pread_structured"; Link "aio_pread";
fb811a
+                Link "aio_pread_structured"];
fb811a
+  };
fb811a
+
fb811a
+  "get_pread_initialize", {
fb811a
+    default_call with
fb811a
+    args = []; ret = RBool;
fb811a
+    may_set_error = false;
fb811a
+    shortdesc = "see whether libnbd pre-initializes read buffers";
fb811a
+    longdesc = "\
fb811a
+Return whether libnbd performs a pre-initialization of a buffer passed
fb811a
+to L<nbd_pread(3)> and similar to all zeroes, as set by
fb811a
+L<nbd_set_pread_initialize(3)>.";
fb811a
+    see_also = [Link "set_pread_initialize";
fb811a
+                Link "set_strict_mode";
fb811a
+                Link "pread"; Link "pread_structured"; Link "aio_pread";
fb811a
+                Link "aio_pread_structured"];
fb811a
+  };
fb811a
+
fb811a
   "set_strict_mode", {
fb811a
     default_call with
fb811a
     args = [ Flags ("flags", strict_flags) ]; ret = RErr;
fb811a
@@ -1825,11 +1868,16 @@ C<LIBNBD_CMD_FLAG_DF>.
fb811a
 The C<flags> parameter must be C<0> for now (it exists for future NBD
fb811a
 protocol extensions).
fb811a
 
fb811a
-Note that if this command fails, it is unspecified whether the contents
fb811a
-of C<buf> will read as zero or as partial results from the server."
fb811a
+Note that if this command fails, and L<nbd_get_pread_initialize(3)>
fb811a
+returns true, then libnbd sanitized C<buf>, but it is unspecified
fb811a
+whether the contents of C<buf> will read as zero or as partial results
fb811a
+from the server.  If L<nbd_get_pread_initialize(3)> returns false,
fb811a
+then libnbd did not sanitize C<buf>, and the contents are undefined
fb811a
+on failure."
fb811a
 ^ strict_call_description;
fb811a
     see_also = [Link "aio_pread"; Link "pread_structured";
fb811a
-                Link "get_block_size"; Link "set_strict_mode"];
fb811a
+                Link "get_block_size"; Link "set_strict_mode";
fb811a
+                Link "set_pread_initialize"];
fb811a
     example = Some "examples/fetch-first-sector.c";
fb811a
   };
fb811a
 
fb811a
@@ -1907,12 +1955,16 @@ more than one fragment (if that is supported - some servers cannot do
fb811a
 this, see L<nbd_can_df(3)>). Libnbd does not validate that the server
fb811a
 actually obeys the flag.
fb811a
 
fb811a
-Note that if this command fails, it is unspecified whether the contents
fb811a
-of C<buf> will read as zero or as partial results from the server."
fb811a
+Note that if this command fails, and L<nbd_get_pread_initialize(3)>
fb811a
+returns true, then libnbd sanitized C<buf>, but it is unspecified
fb811a
+whether the contents of C<buf> will read as zero or as partial results
fb811a
+from the server.  If L<nbd_get_pread_initialize(3)> returns false,
fb811a
+then libnbd did not sanitize C<buf>, and the contents are undefined
fb811a
+on failure."
fb811a
 ^ strict_call_description;
fb811a
     see_also = [Link "can_df"; Link "pread";
fb811a
                 Link "aio_pread_structured"; Link "get_block_size";
fb811a
-                Link "set_strict_mode"];
fb811a
+                Link "set_strict_mode"; Link "set_pread_initialize"];
fb811a
   };
fb811a
 
fb811a
   "pwrite", {
fb811a
@@ -2420,14 +2472,19 @@ as described in L<libnbd(3)/Completion callbacks>.
fb811a
 Note that you must ensure C<buf> is valid until the command has
fb811a
 completed.  Furthermore, if the C<error> parameter to
fb811a
 C<completion_callback> is set or if L<nbd_aio_command_completed(3)>
fb811a
-reports failure, it is unspecified whether the contents
fb811a
-of C<buf> will read as zero or as partial results from the server.
fb811a
+reports failure, and if L<nbd_get_pread_initialize(3)> returns true,
fb811a
+then libnbd sanitized C<buf>, but it is unspecified whether the
fb811a
+contents of C<buf> will read as zero or as partial results from the
fb811a
+server.  If L<nbd_get_pread_initialize(3)> returns false, then
fb811a
+libnbd did not sanitize C<buf>, and the contents are undefined
fb811a
+on failure.
fb811a
+
fb811a
 Other parameters behave as documented in L<nbd_pread(3)>."
fb811a
 ^ strict_call_description;
fb811a
     example = Some "examples/aio-connect-read.c";
fb811a
     see_also = [SectionLink "Issuing asynchronous commands";
fb811a
                 Link "aio_pread_structured"; Link "pread";
fb811a
-                Link "set_strict_mode"];
fb811a
+                Link "set_strict_mode"; Link "set_pread_initialize"];
fb811a
   };
fb811a
 
fb811a
   "aio_pread_structured", {
fb811a
@@ -2449,13 +2506,18 @@ as described in L<libnbd(3)/Completion callbacks>.
fb811a
 Note that you must ensure C<buf> is valid until the command has
fb811a
 completed.  Furthermore, if the C<error> parameter to
fb811a
 C<completion_callback> is set or if L<nbd_aio_command_completed(3)>
fb811a
-reports failure, it is unspecified whether the contents
fb811a
-of C<buf> will read as zero or as partial results from the server.
fb811a
+reports failure, and if L<nbd_get_pread_initialize(3)> returns true,
fb811a
+then libnbd sanitized C<buf>, but it is unspecified whether the
fb811a
+contents of C<buf> will read as zero or as partial results from the
fb811a
+server.  If L<nbd_get_pread_initialize(3)> returns false, then
fb811a
+libnbd did not sanitize C<buf>, and the contents are undefined
fb811a
+on failure.
fb811a
+
fb811a
 Other parameters behave as documented in L<nbd_pread_structured(3)>."
fb811a
 ^ strict_call_description;
fb811a
     see_also = [SectionLink "Issuing asynchronous commands";
fb811a
                 Link "aio_pread"; Link "pread_structured";
fb811a
-                Link "set_strict_mode"];
fb811a
+                Link "set_strict_mode"; Link "set_pread_initialize"];
fb811a
   };
fb811a
 
fb811a
   "aio_pwrite", {
fb811a
@@ -3093,6 +3155,10 @@ let first_version = [
fb811a
   "get_private_data", (1, 8);
fb811a
   "get_uri", (1, 8);
fb811a
 
fb811a
+  (* Added in 1.11.x development cycle, will be stable and supported in 1.12. *)
fb811a
+  "set_pread_initialize", (1, 12);
fb811a
+  "get_pread_initialize", (1, 12);
fb811a
+
fb811a
   (* These calls are proposed for a future version of libnbd, but
fb811a
    * have not been added to any released version so far.
fb811a
   "get_tls_certificates", (1, ??);
fb811a
diff --git a/generator/C.ml b/generator/C.ml
fb811a
index 4a5bb58..2b6198c 100644
fb811a
--- a/generator/C.ml
fb811a
+++ b/generator/C.ml
fb811a
@@ -496,7 +496,8 @@ let generate_lib_api_c () =
fb811a
       function
fb811a
       | BytesOut (n, count)
fb811a
       | BytesPersistOut (n, count) ->
fb811a
-         pr "  memset (%s, 0, %s);\n" n count
fb811a
+         pr "  if (h->pread_initialize)\n";
fb811a
+         pr "    memset (%s, 0, %s);\n" n count
fb811a
       | _ -> ()
fb811a
     ) args;
fb811a
 
fb811a
diff --git a/golang/src/libguestfs.org/libnbd/libnbd_110_defaults_test.go b/golang/src/libguestfs.org/libnbd/libnbd_110_defaults_test.go
fb811a
index b3ceb45..ca7c1c4 100644
fb811a
--- a/golang/src/libguestfs.org/libnbd/libnbd_110_defaults_test.go
fb811a
+++ b/golang/src/libguestfs.org/libnbd/libnbd_110_defaults_test.go
fb811a
@@ -1,5 +1,5 @@
fb811a
 /* libnbd golang tests
fb811a
- * Copyright (C) 2013-2021 Red Hat Inc.
fb811a
+ * Copyright (C) 2013-2022 Red Hat Inc.
fb811a
  *
fb811a
  * This library is free software; you can redistribute it and/or
fb811a
  * modify it under the terms of the GNU Lesser General Public
fb811a
@@ -59,6 +59,14 @@ func Test110Defaults(t *testing.T) {
fb811a
 		t.Fatalf("unexpected structured replies state")
fb811a
 	}
fb811a
 
fb811a
+	init, err := h.GetPreadInitialize()
fb811a
+	if err != nil {
fb811a
+		t.Fatalf("could not get pread initialize state: %s", err)
fb811a
+	}
fb811a
+	if init != true {
fb811a
+		t.Fatalf("unexpected pread initialize state")
fb811a
+	}
fb811a
+
fb811a
 	flags, err := h.GetHandshakeFlags()
fb811a
 	if err != nil {
fb811a
 		t.Fatalf("could not get handshake flags: %s", err)
fb811a
diff --git a/golang/src/libguestfs.org/libnbd/libnbd_120_set_non_defaults_test.go b/golang/src/libguestfs.org/libnbd/libnbd_120_set_non_defaults_test.go
fb811a
index f112456..029f0db 100644
fb811a
--- a/golang/src/libguestfs.org/libnbd/libnbd_120_set_non_defaults_test.go
fb811a
+++ b/golang/src/libguestfs.org/libnbd/libnbd_120_set_non_defaults_test.go
fb811a
@@ -93,6 +93,18 @@ func Test120SetNonDefaults(t *testing.T) {
fb811a
 		t.Fatalf("unexpected structured replies state")
fb811a
 	}
fb811a
 
fb811a
+	err = h.SetPreadInitialize(false)
fb811a
+	if err != nil {
fb811a
+		t.Fatalf("could not set pread initialize state: %s", err)
fb811a
+	}
fb811a
+	init, err := h.GetPreadInitialize()
fb811a
+	if err != nil {
fb811a
+		t.Fatalf("could not get pread initialize state: %s", err)
fb811a
+	}
fb811a
+	if init != false {
fb811a
+		t.Fatalf("unexpected pread initialize state")
fb811a
+	}
fb811a
+
fb811a
 	err = h.SetHandshakeFlags(HANDSHAKE_FLAG_MASK + 1)
fb811a
 	if err == nil {
fb811a
 		t.Fatalf("expect failure for out-of-range flags")
fb811a
diff --git a/lib/handle.c b/lib/handle.c
fb811a
index 67aa875..ac6c16e 100644
fb811a
--- a/lib/handle.c
fb811a
+++ b/lib/handle.c
fb811a
@@ -1,5 +1,5 @@
fb811a
 /* NBD client library in userspace
fb811a
- * Copyright (C) 2013-2020 Red Hat Inc.
fb811a
+ * Copyright (C) 2013-2022 Red Hat Inc.
fb811a
  *
fb811a
  * This library is free software; you can redistribute it and/or
fb811a
  * modify it under the terms of the GNU Lesser General Public
fb811a
@@ -64,6 +64,7 @@ nbd_create (void)
fb811a
   h->unique = 1;
fb811a
   h->tls_verify_peer = true;
fb811a
   h->request_sr = true;
fb811a
+  h->pread_initialize = true;
fb811a
 
fb811a
   h->uri_allow_transports = LIBNBD_ALLOW_TRANSPORT_MASK;
fb811a
   h->uri_allow_tls = LIBNBD_TLS_ALLOW;
fb811a
@@ -393,6 +394,20 @@ nbd_unlocked_get_handshake_flags (struct nbd_handle *h)
fb811a
   return h->gflags;
fb811a
 }
fb811a
 
fb811a
+int
fb811a
+nbd_unlocked_set_pread_initialize (struct nbd_handle *h, bool request)
fb811a
+{
fb811a
+  h->pread_initialize = request;
fb811a
+  return 0;
fb811a
+}
fb811a
+
fb811a
+/* NB: may_set_error = false. */
fb811a
+int
fb811a
+nbd_unlocked_get_pread_initialize (struct nbd_handle *h)
fb811a
+{
fb811a
+  return h->pread_initialize;
fb811a
+}
fb811a
+
fb811a
 int
fb811a
 nbd_unlocked_set_strict_mode (struct nbd_handle *h, uint32_t flags)
fb811a
 {
fb811a
diff --git a/lib/internal.h b/lib/internal.h
fb811a
index 0e205ab..525499a 100644
fb811a
--- a/lib/internal.h
fb811a
+++ b/lib/internal.h
fb811a
@@ -1,5 +1,5 @@
fb811a
 /* nbd client library in userspace: internal definitions
fb811a
- * Copyright (C) 2013-2020 Red Hat Inc.
fb811a
+ * Copyright (C) 2013-2022 Red Hat Inc.
fb811a
  *
fb811a
  * This library is free software; you can redistribute it and/or
fb811a
  * modify it under the terms of the GNU Lesser General Public
fb811a
@@ -123,6 +123,9 @@ struct nbd_handle {
fb811a
   /* Full info mode. */
fb811a
   bool full_info;
fb811a
 
fb811a
+  /* Sanitization for pread. */
fb811a
+  bool pread_initialize;
fb811a
+
fb811a
   /* Global flags from the server. */
fb811a
   uint16_t gflags;
fb811a
 
fb811a
diff --git a/ocaml/tests/test_110_defaults.ml b/ocaml/tests/test_110_defaults.ml
fb811a
index b36949f..04aa744 100644
fb811a
--- a/ocaml/tests/test_110_defaults.ml
fb811a
+++ b/ocaml/tests/test_110_defaults.ml
fb811a
@@ -1,6 +1,6 @@
fb811a
 (* hey emacs, this is OCaml code: -*- tuareg -*- *)
fb811a
 (* libnbd OCaml test case
fb811a
- * Copyright (C) 2013-2020 Red Hat Inc.
fb811a
+ * Copyright (C) 2013-2022 Red Hat Inc.
fb811a
  *
fb811a
  * This library is free software; you can redistribute it and/or
fb811a
  * modify it under the terms of the GNU Lesser General Public
fb811a
@@ -28,6 +28,8 @@ let () =
fb811a
       assert (tls = NBD.TLS.DISABLE);
fb811a
       let sr = NBD.get_request_structured_replies nbd in
fb811a
       assert (sr = true);
fb811a
+      let init = NBD.get_pread_initialize nbd in
fb811a
+      assert (init = true);
fb811a
       let flags = NBD.get_handshake_flags nbd in
fb811a
       assert (flags = NBD.HANDSHAKE_FLAG.mask);
fb811a
       let opt = NBD.get_opt_mode nbd in
fb811a
diff --git a/ocaml/tests/test_120_set_non_defaults.ml b/ocaml/tests/test_120_set_non_defaults.ml
fb811a
index 67928bb..f949807 100644
fb811a
--- a/ocaml/tests/test_120_set_non_defaults.ml
fb811a
+++ b/ocaml/tests/test_120_set_non_defaults.ml
fb811a
@@ -1,6 +1,6 @@
fb811a
 (* hey emacs, this is OCaml code: -*- tuareg -*- *)
fb811a
 (* libnbd OCaml test case
fb811a
- * Copyright (C) 2013-2020 Red Hat Inc.
fb811a
+ * Copyright (C) 2013-2022 Red Hat Inc.
fb811a
  *
fb811a
  * This library is free software; you can redistribute it and/or
fb811a
  * modify it under the terms of the GNU Lesser General Public
fb811a
@@ -42,6 +42,9 @@ let () =
fb811a
       NBD.set_request_structured_replies nbd false;
fb811a
       let sr = NBD.get_request_structured_replies nbd in
fb811a
       assert (sr = false);
fb811a
+      NBD.set_pread_initialize nbd false;
fb811a
+      let init = NBD.get_pread_initialize nbd in
fb811a
+      assert (init = false);
fb811a
       (try
fb811a
          NBD.set_handshake_flags nbd [ NBD.HANDSHAKE_FLAG.UNKNOWN 2 ];
fb811a
          assert false
fb811a
diff --git a/python/t/110-defaults.py b/python/t/110-defaults.py
fb811a
index fb961cf..a4262da 100644
fb811a
--- a/python/t/110-defaults.py
fb811a
+++ b/python/t/110-defaults.py
fb811a
@@ -1,5 +1,5 @@
fb811a
 # libnbd Python bindings
fb811a
-# Copyright (C) 2010-2020 Red Hat Inc.
fb811a
+# Copyright (C) 2010-2022 Red Hat Inc.
fb811a
 #
fb811a
 # This program is free software; you can redistribute it and/or modify
fb811a
 # it under the terms of the GNU General Public License as published by
fb811a
@@ -22,5 +22,6 @@ assert h.get_export_name() == ""
fb811a
 assert h.get_full_info() is False
fb811a
 assert h.get_tls() == nbd.TLS_DISABLE
fb811a
 assert h.get_request_structured_replies() is True
fb811a
+assert h.get_pread_initialize() is True
fb811a
 assert h.get_handshake_flags() == nbd.HANDSHAKE_FLAG_MASK
fb811a
 assert h.get_opt_mode() is False
fb811a
diff --git a/python/t/120-set-non-defaults.py b/python/t/120-set-non-defaults.py
fb811a
index 3da0c23..e71c6ad 100644
fb811a
--- a/python/t/120-set-non-defaults.py
fb811a
+++ b/python/t/120-set-non-defaults.py
fb811a
@@ -1,5 +1,5 @@
fb811a
 # libnbd Python bindings
fb811a
-# Copyright (C) 2010-2020 Red Hat Inc.
fb811a
+# Copyright (C) 2010-2022 Red Hat Inc.
fb811a
 #
fb811a
 # This program is free software; you can redistribute it and/or modify
fb811a
 # it under the terms of the GNU General Public License as published by
fb811a
@@ -33,6 +33,8 @@ if h.supports_tls():
fb811a
     assert h.get_tls() == nbd.TLS_ALLOW
fb811a
 h.set_request_structured_replies(False)
fb811a
 assert h.get_request_structured_replies() is False
fb811a
+h.set_pread_initialize(False)
fb811a
+assert h.get_pread_initialize() is False
fb811a
 try:
fb811a
     h.set_handshake_flags(nbd.HANDSHAKE_FLAG_MASK + 1)
fb811a
     assert False
fb811a
diff --git a/tests/errors.c b/tests/errors.c
fb811a
index f597b7e..0298da8 100644
fb811a
--- a/tests/errors.c
fb811a
+++ b/tests/errors.c
fb811a
@@ -213,7 +213,15 @@ main (int argc, char *argv[])
fb811a
   }
fb811a
 
fb811a
 
fb811a
-  /* Issue a connected command when not connected. */
fb811a
+  /* Issue a connected command when not connected. pread_initialize defaults
fb811a
+   * to set.
fb811a
+   */
fb811a
+  if (nbd_get_pread_initialize (nbd) != 1) {
fb811a
+    fprintf (stderr, "%s: test failed: "
fb811a
+             "nbd_get_pread_initialize gave unexpected result\n",
fb811a
+             argv[0]);
fb811a
+    exit (EXIT_FAILURE);
fb811a
+  }
fb811a
   buf[0] = '1';
fb811a
   if (nbd_pread (nbd, buf, 512, 0, 0) != -1) {
fb811a
     fprintf (stderr, "%s: test failed: "
fb811a
@@ -294,7 +302,14 @@ main (int argc, char *argv[])
fb811a
   }
fb811a
   check (EINVAL, "nbd_aio_command_completed: ");
fb811a
 
fb811a
-  /* Read from an invalid offset, client-side */
fb811a
+  /* Read from an invalid offset, client-side. When pread_initialize is off,
fb811a
+   * libnbd should not have touched our buffer.
fb811a
+   */
fb811a
+  if (nbd_set_pread_initialize (nbd, false) == -1) {
fb811a
+    fprintf (stderr, "%s\n", nbd_get_error ());
fb811a
+    exit (EXIT_FAILURE);
fb811a
+  }
fb811a
+  buf[0] = '1';
fb811a
   strict = nbd_get_strict_mode (nbd) | LIBNBD_STRICT_BOUNDS;
fb811a
   if (nbd_set_strict_mode (nbd, strict) == -1) {
fb811a
     fprintf (stderr, "%s\n", nbd_get_error ());
fb811a
@@ -307,6 +322,12 @@ main (int argc, char *argv[])
fb811a
     exit (EXIT_FAILURE);
fb811a
   }
fb811a
   check (EINVAL, "nbd_aio_pread: ");
fb811a
+  if (buf[0] != '1') {
fb811a
+    fprintf (stderr, "%s: test failed: "
fb811a
+             "nbd_pread incorrectly sanitized buffer on client-side error\n",
fb811a
+             argv[0]);
fb811a
+    exit (EXIT_FAILURE);
fb811a
+  }
fb811a
 
fb811a
   /* We guarantee callbacks will be freed even on all error paths. */
fb811a
   if (nbd_aio_pread_structured (nbd, buf, 512, -1,
fb811a
-- 
fb811a
2.31.1
fb811a