Richard W.M. Jones
2019-May-23 09:32 UTC
[Libguestfs] [PATCH libnbd 0/3] Prevent some misuse of multi-conn.
Per recent discussion here: https://www.redhat.com/archives/libguestfs/2019-May/thread.html#00175
Richard W.M. Jones
2019-May-23 09:32 UTC
[Libguestfs] [PATCH libnbd 1/3] states: Factor out common code for setting export size and eflags.
Simple refactoring. --- generator/states-newstyle-opt-export-name.c | 12 +++++------ generator/states-newstyle-opt-go.c | 13 ++++++------ generator/states-oldstyle.c | 10 +++------- lib/flags.c | 22 +++++++++++++++++++++ lib/internal.h | 5 +++++ 5 files changed, 42 insertions(+), 20 deletions(-) diff --git a/generator/states-newstyle-opt-export-name.c b/generator/states-newstyle-opt-export-name.c index 8ff1c1c..07b6c9e 100644 --- a/generator/states-newstyle-opt-export-name.c +++ b/generator/states-newstyle-opt-export-name.c @@ -58,13 +58,13 @@ return 0; NEWSTYLE.OPT_EXPORT_NAME.CHECK_REPLY: - conn->h->exportsize = be64toh (conn->sbuf.export_name_reply.exportsize); - conn->h->eflags = be16toh (conn->sbuf.export_name_reply.eflags); - debug (conn->h, "exportsize: %" PRIu64 " eflags: 0x%" PRIx16, - conn->h->exportsize, conn->h->eflags); - if (conn->h->eflags == 0) { + uint64_t exportsize; + uint16_t eflags; + + exportsize = be64toh (conn->sbuf.export_name_reply.exportsize); + eflags = be16toh (conn->sbuf.export_name_reply.eflags); + if (nbd_internal_set_size_and_flags (conn->h, exportsize, eflags) == -1) { SET_NEXT_STATE (%.DEAD); - set_error (EINVAL, "handshake: invalid eflags == 0 from server"); return -1; } SET_NEXT_STATE (%.READY); diff --git a/generator/states-newstyle-opt-go.c b/generator/states-newstyle-opt-go.c index 4dbeee9..97f25f8 100644 --- a/generator/states-newstyle-opt-go.c +++ b/generator/states-newstyle-opt-go.c @@ -106,6 +106,8 @@ uint32_t reply; uint32_t len; const size_t maxpayload = sizeof conn->sbuf.or.payload; + uint64_t exportsize; + uint16_t eflags; magic = be64toh (conn->sbuf.or.option_reply.magic); option = be32toh (conn->sbuf.or.option_reply.option); @@ -129,14 +131,11 @@ if (len <= maxpayload /* see RECV_NEWSTYLE_OPT_GO_REPLY */) { if (len >= sizeof conn->sbuf.or.payload.export) { if (be16toh (conn->sbuf.or.payload.export.info) == NBD_INFO_EXPORT) { - conn->h->exportsize - be64toh (conn->sbuf.or.payload.export.exportsize); - conn->h->eflags = be16toh (conn->sbuf.or.payload.export.eflags); - debug (conn->h, "exportsize: %" PRIu64 " eflags: 0x%" PRIx16, - conn->h->exportsize, conn->h->eflags); - if (conn->h->eflags == 0) { + exportsize = be64toh (conn->sbuf.or.payload.export.exportsize); + eflags = be16toh (conn->sbuf.or.payload.export.eflags); + if (nbd_internal_set_size_and_flags (conn->h, + exportsize, eflags) == -1) { SET_NEXT_STATE (%.DEAD); - set_error (EINVAL, "handshake: invalid eflags == 0 from server"); return -1; } } diff --git a/generator/states-oldstyle.c b/generator/states-oldstyle.c index 95b7df9..29cb341 100644 --- a/generator/states-oldstyle.c +++ b/generator/states-oldstyle.c @@ -47,14 +47,10 @@ eflags = be16toh (conn->sbuf.old_handshake.eflags); conn->gflags = gflags; - conn->h->exportsize = exportsize; - conn->h->eflags = eflags; - debug (conn->h, "exportsize: %" PRIu64 " eflags: 0x%" PRIx16 - " gflags: 0x%" PRIx16, - exportsize, eflags, gflags); - if (eflags == 0) { + debug (conn->h, "gflags: 0x%" PRIx16, gflags); + + if (nbd_internal_set_size_and_flags (conn->h, exportsize, eflags) == -1) { SET_NEXT_STATE (%.DEAD); - set_error (EINVAL, "handshake: invalid eflags == 0 from server"); return -1; } diff --git a/lib/flags.c b/lib/flags.c index f0d9dc3..825b326 100644 --- a/lib/flags.c +++ b/lib/flags.c @@ -21,10 +21,32 @@ #include <stdio.h> #include <stdlib.h> #include <stdbool.h> +#include <stdint.h> +#include <inttypes.h> #include <errno.h> #include "internal.h" +/* Set the export size and eflags on the handle, validating them. + * This is called from the state machine when either the newstyle or + * oldstyle negotiation reaches the point that these are available. + */ +int +nbd_internal_set_size_and_flags (struct nbd_handle *h, + uint64_t exportsize, uint16_t eflags) +{ + debug (h, "exportsize: %" PRIu64 " eflags: 0x%" PRIx16, exportsize, eflags); + + if (eflags == 0) { + set_error (EINVAL, "handshake: invalid eflags == 0 from server"); + return -1; + } + + h->exportsize = exportsize; + h->eflags = eflags; + return 0; +} + static int get_flag (struct nbd_handle *h, uint16_t flag) { diff --git a/lib/internal.h b/lib/internal.h index 8eadada..f0705ef 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -265,6 +265,11 @@ extern void nbd_internal_set_last_error (int errnum, char *error); nbd_internal_set_last_error ((errnum), _errp); \ } while (0) +/* flags.c */ +extern int nbd_internal_set_size_and_flags (struct nbd_handle *h, + uint64_t exportsize, + uint16_t eflags); + /* protocol.c */ extern int nbd_internal_errno_of_nbd_error (uint32_t error); extern const char *nbd_internal_name_of_nbd_cmd (uint16_t type); -- 2.21.0
Richard W.M. Jones
2019-May-23 09:32 UTC
[Libguestfs] [PATCH libnbd 2/3] states: Prevent multi-conn connection unless the server advertizes it.
It is unsafe to connect using multi-conn to a server unless the server advertizes it, so fail if the caller tries to do this. --- generator/generator | 16 ++++++++++++++-- lib/flags.c | 10 ++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/generator/generator b/generator/generator index a372074..a8e6fb6 100755 --- a/generator/generator +++ b/generator/generator @@ -897,7 +897,8 @@ The C<multi_conn> parameter controls whether this feature is enabled (if E<gt> 1) or disabled (if C<1>). The parameter passed must not be C<0>. Usually small powers of 2 (eg. 2, 4, 8) will provide increments in performance. Some servers do not -support this feature and will return an error on connection. +support this feature and libnbd will fail on connection to +these servers if this setting is E<gt> 1. At present, libnbd assumes that all connections will report the same capabilities; a server that behaves otherwise may trigger @@ -1171,7 +1172,18 @@ connected to and completed the handshake with the server."; Returns true if the server supports multi-conn (see C<nbd_set_multi_conn>). Returns false if the server does not. Can return an error if we have not -connected to and completed the handshake with the server."; +connected to and completed the handshake with the server. + +Note that you I<cannot> set multi-conn on the handle +to E<gt> 1, connect, and then check this setting, because +if the server does not support multi-conn the connection +will fail. Instead you should connect with multi-conn +set to C<1> (the default), check this setting, then if +multi-conn is found to be supported you can call +C<nbd_set_multi_conn> with a larger value to increase +the number of connection objects, then call one of the +C<nbd_connect*> functions again on the handle to connect +the remaining connections."; }; "can_cache", { diff --git a/lib/flags.c b/lib/flags.c index 825b326..1956db6 100644 --- a/lib/flags.c +++ b/lib/flags.c @@ -42,6 +42,16 @@ nbd_internal_set_size_and_flags (struct nbd_handle *h, return -1; } + /* It is unsafe to connect to a server with multi-conn set unless + * the server says it is safe to do so. + */ + if (h->multi_conn > 1 && (eflags & NBD_FLAG_CAN_MULTI_CONN) == 0) { + set_error (EINVAL, "handshake: multi-conn is set on this handle, " + "but the server does not advertize multi-conn support " + "so disconnecting because it is not safe to continue"); + return -1; + } + h->exportsize = exportsize; h->eflags = eflags; return 0; -- 2.21.0
Richard W.M. Jones
2019-May-23 09:32 UTC
[Libguestfs] [PATCH libnbd 3/3] lib/connect.c: Fail synchronous nbd_connect_command if multi-conn is set.
However we don't modify the asynchronous call, continuing the general policy of allowing users to do what they want with AIO even if it is unwise. --- lib/connect.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/connect.c b/lib/connect.c index c5970d2..61d945f 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -150,12 +150,15 @@ nbd_unlocked_connect_tcp (struct nbd_handle *h, return wait_all_connected (h); } -/* Connect to a local command. Multi-conn doesn't make much sense - * here, should it be an error? - */ +/* Connect to a local command. */ int nbd_unlocked_connect_command (struct nbd_handle *h, char **argv) { + if (h->multi_conn > 1) { + set_error (EINVAL, "multi-conn cannot be used when connecting to a command"); + return -1; + } + if (!nbd_unlocked_aio_is_created (h->conns[0])) { set_error (0, "first connection in this handle is not in the created state, this is likely to be caused by a programming error in the calling program"); return -1; -- 2.21.0
Eric Blake
2019-May-23 12:25 UTC
Re: [Libguestfs] [PATCH libnbd 2/3] states: Prevent multi-conn connection unless the server advertizes it.
On 5/23/19 4:32 AM, Richard W.M. Jones wrote:> It is unsafe to connect using multi-conn to a server unless the server > advertizes it, so fail if the caller tries to do this.Actually, I think we can be a little bit looser: It is unsafe to connect with write access using multi-conn. But for read-only (where we can't corrupt data), having multiple connections even when the server does not advertise multi-conn should (in general) still be safe.> --- > generator/generator | 16 ++++++++++++++-- > lib/flags.c | 10 ++++++++++ > 2 files changed, 24 insertions(+), 2 deletions(-) > > diff --git a/generator/generator b/generator/generator > index a372074..a8e6fb6 100755 > --- a/generator/generator > +++ b/generator/generator > @@ -897,7 +897,8 @@ The C<multi_conn> parameter controls whether this feature is > enabled (if E<gt> 1) or disabled (if C<1>). The parameter > passed must not be C<0>. Usually small powers of 2 (eg. 2, 4, 8) > will provide increments in performance. Some servers do not > -support this feature and will return an error on connection. > +support this feature and libnbd will fail on connection to > +these servers if this setting is E<gt> 1.So maybe change this to: ...libnbd will fail on read-write connections to these servers if...> > At present, libnbd assumes that all connections will report the > same capabilities; a server that behaves otherwise may trigger > @@ -1171,7 +1172,18 @@ connected to and completed the handshake with the server."; > Returns true if the server supports multi-conn > (see C<nbd_set_multi_conn>). Returns false if > the server does not. Can return an error if we have not > -connected to and completed the handshake with the server."; > +connected to and completed the handshake with the server. > + > +Note that you I<cannot> set multi-conn on the handle > +to E<gt> 1, connect, and then check this setting, because > +if the server does not support multi-conn the connection > +will fail. Instead you should connect with multi-conn > +set to C<1> (the default), check this setting, then if > +multi-conn is found to be supported you can call > +C<nbd_set_multi_conn> with a larger value to increase > +the number of connection objects, then call one of the > +C<nbd_connect*> functions again on the handle to connect > +the remaining connections.";This one is good.> }; > > "can_cache", { > diff --git a/lib/flags.c b/lib/flags.c > index 825b326..1956db6 100644 > --- a/lib/flags.c > +++ b/lib/flags.c > @@ -42,6 +42,16 @@ nbd_internal_set_size_and_flags (struct nbd_handle *h, > return -1; > } > > + /* It is unsafe to connect to a server with multi-conn set unless > + * the server says it is safe to do so. > + */ > + if (h->multi_conn > 1 && (eflags & NBD_FLAG_CAN_MULTI_CONN) == 0) {Here, I'd add && !(eflags & NBD_FLAG_READ_ONLY)> + set_error (EINVAL, "handshake: multi-conn is set on this handle, " > + "but the server does not advertize multi-conn support "I know that in general -ise vs. -ize is a common US/UK thing, but https://english.stackexchange.com/questions/341707/should-advertised-be-spelled-with-a-z-in-american-english says that advertise is preferred in both. -- Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3226 Virtualization: qemu.org | libvirt.org
Eric Blake
2019-May-23 12:33 UTC
Re: [Libguestfs] [PATCH libnbd 1/3] states: Factor out common code for setting export size and eflags.
On 5/23/19 4:32 AM, Richard W.M. Jones wrote:> Simple refactoring. > --- > generator/states-newstyle-opt-export-name.c | 12 +++++------ > generator/states-newstyle-opt-go.c | 13 ++++++------ > generator/states-oldstyle.c | 10 +++------- > lib/flags.c | 22 +++++++++++++++++++++ > lib/internal.h | 5 +++++ > 5 files changed, 42 insertions(+), 20 deletions(-)ACK. Odd that the diffstat doesn't match that this does look simpler in the end. It also gives us a common hook point to enforce (which we may want - although we're still deciding that) that secondary multi-conn connections come up with the same eflags as the first. -- Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3226 Virtualization: qemu.org | libvirt.org
Maybe Matching Threads
- [PATCH libnbd] api: Get rid of nbd_connection.
- [libnbd PATCH] api: Add set_handshake_flags for integration
- [libnbd PATCH 0/4] Various interop fixes
- [libnbd PATCH 0/7] state machine refactoring
- [PATCH libnbd 1/3] states: Factor out common code for setting export size and eflags.