On 10/14/2016 01:30 PM, Wouter Verhelst wrote:
>>> It's not been tested yet, however, because the client side hasn't been
>>> done yet.
>> It has at least one bug, from what I've quickly seen.  You HAVE to parse
>> off length and any data the client sends before you can try to read the
>> next option; you have this bug in multiple places.
> Whoops.

>  static void socket_read(CLIENT* client, void *buf, size_t len) {
> +     void *tmp = NULL;
> +     if (!buf) {
> +             /* FIXME: Enforce maximum bound on client-provided len? */
> +             tmp = buf = malloc(len);
> +     }
>       g_assert(client->socket_read != NULL);

Any thoughts about this one? Should we enforce a maximum client option
length (such as: any client that wants to send us an NBD_OPT_* with more
than 1M payload is trying to DoS us)?  If so, what is an appropriate
limit?  We've sort of had the conversation in a past thread about
maximum limits that a server must tolerate before declaring the client
to be a waste of time (such as how many error messages can a server send
before deciding the client isn't going to ask a successful question; how
much data in NBD_OPT is too much, while still permitting our spec of 64k
strings as acceptable and assuming we may add a future NBD_OPT that has
multiple strings, etc).  At one point, I had even proposed a patch to
force length to be 16 bits (freeing up 16 other bits that could be used
for future expansion for some other purpose), but as I recall you didn't
like the idea.

Since we are malloc'ing a scratch buffer to hold a client-specified
length, I do NOT want us to be casually allowing the client to tell us
to make a 2G allocation.  Maybe when reading off dead length, it's
better to write a loop that does a loop into a max-size buffer for as
many loop iterations as needed, rather than allocating a single buffer
that will just be thrown away; but such complexity doesn't belong on the
hot-path of normal reads.  Still, even if I cap maximum allocation by
reading in a loop, there's a question of how much time we allow to
processing dead reads, vs. cutting our losses and disconnecting the
client as ill-behaved.

>>                              send_reply(client, opt, NBD_REP_ERR_INVALID, 
>> -1, "Invalid STARTTLS
>> request: TLS has already been negotiated!");
>> +                            socket_read(client, &len, sizeof(len));
>> +                            len = ntohl(len);
>> +                            if(len) {
>> +                                    socket_read(client, NULL, len);
> This should probably send NBD_REP_ERR_INVALID, though (since STARTTLS does not
> allow data).

But we've already sent NBD_REP_ERR_INVALID in the line just before we
detect the invalid length (note: the guest can't tell whether we replied
with that error because of invalid length or because of requesting at an
invalid point in the handshake protocol).

Since I repeated a common construct across multiple lines, I may create
a helper function to parse off length.  Especially if I want to add
complexity for handling over-long user-supplied lengths without a huge

>> +                            }
>>                              continue;
>>                      }
>>                      if(tlsdir == NULL) {
>>                              send_reply(client, opt, NBD_REP_ERR_POLICY, -1, 
>> "TLS not allowed on
>> this server");
>> +                            socket_read(client, &len, sizeof(len));
>> +                            len = ntohl(len);
>> +                            if(len) {
>> +                                    socket_read(client, NULL, len);
> Same here.

Okay, here we have a case where two errors are both possible at the same
time: NBD_REP_ERR_POLICY (that the code just sent in the line earlier)
and NBD_REP_ERR_INVALID (for bad length).  The protocol doesn't state
any priority scheme when multiple errors are simultaneously possible,
does it?  I don't see that it makes any particular difference, although
it may mean that we want to add a sentence to the protocol clarifying
that if more than one error makes sense, a client MUST NOT rely on the
server to send a specific error, only that an error will be sent.

> Other than that, looks good, but can you send that with git format-patch (or
> git send-email)?  Makes applying it (with a proper commit message) easier.

Well yeah, that's the plan, once I can build it to actually test it. :)
I'm still plugging away at the autoreconf failure on my end.  This was
just the quick PoC to get the conversation going, pointing out that you
made a common mistake (since I've already fixed the same bug in both
qemu and nbdkit); and that it is the same mistake that
NBD_FLAG_FIXED_NEWSTYLE is supposed to advertise has been fixed.  It is
absolutely vital to the handshake protocol that the server read off
exactly as much as the client sent even when replying with an error, in
order to be back in sync for reading the next command from the client.

Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org

Attachment: signature.asc
Description: OpenPGP digital signature

Check out the vibrant tech community on one of the world's most 
engaging tech sites, SlashDot.org! http://sdm.link/slashdot
Nbd-general mailing list

Reply via email to