Bug #101479 mysql-connector-python protocol violation
Submitted: 5 Nov 2020 14:05 Modified: 11 May 2022 16:14
Reporter: Sergei Golubchik Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / Python Severity:S1 (Critical)
Version:8.0.22 OS:Any
Assigned to: CPU Architecture:Any

[5 Nov 2020 14:05] Sergei Golubchik
Description:
See

https://github.com/mysql/mysql-connector-python/blob/master/lib/mysql/connector/protocol.p...

for COM_STMT_EXECUTE it sends null_bitmap and 1 unconditionally.
According to https://dev.mysql.com/doc/internals/en/com-stmt-execute.html
this should only be done if num-params > 0.

mysql-connector-c behaves correctly.

How to repeat:
See

https://github.com/mysql/mysql-connector-python/blob/master/lib/mysql/connector/protocol.p...

or use tcpdump/wireshark to analyze the packets
[5 Nov 2020 14:15] Sergei Golubchik
mysql-connector-java seems to be having the same issue
[6 Nov 2020 4:21] MySQL Verification Team
Hello Sergei,

Thank you for the report and feedback.

Sincerely,
Umesh
[6 Nov 2020 11:16] Filipe Silva
Hi Sergei,

Actually, the documentation doesn't say exactly what you are claiming.

Please note COM_STMT_EXECUTE payload structure:

  payload:
    1              [17] COM_STMT_EXECUTE
    4              stmt-id
    1              flags
    4              iteration-count
      if num-params > 0:
    n              NULL-bitmap, length: (num-params+7)/8
    1              new-params-bound-flag
      if new-params-bound-flag == 1:
    n              type of each parameter, length: num-params * 2
    n              value of each parameter

As it is written, each one of the 'if' statements affect only the next line, which means that the byte for 'new-params-bound-flag' is supposed to be sent independently of the number of params. If the 'NULL-bitmap' is also being sent by Connector/Python, then yes, this is a bug. OTOH, Connector/J only sends the 'new-params-bound-flag' byte in this case.

Having said that, you are actually correct and clients should not send anything after the first "if" - this is what MySQL protocol expects on the server side -, but, first, this is a documentation bug (the first "if" affects all up the the end), and second, there's no practical effect in sending the extra bytes since the server just skips them. Nevertheless, both documentation and clients should be fixed.

Thanks.
[6 Nov 2020 11:46] Sergei Golubchik
The documentation is a little bit ambiguous, but not much.

If one would think that "each one of the 'if' statements affect only the next line" then one would have to admit that the packet will include "value of each parameter" even when there are no parameters. This is an absurd interpretation, so the syntax — even as it is now — clearly means that 'if' affects both lines or everything to the end.
[6 Nov 2020 12:32] Filipe Silva
But it's exactly that. 

In this same payload for example, the second 'if' affects only the next line, i.e, types for each parameter are only to be sent 'if new-params-bound-flag == 1:', then, regardless of the value of 'new-params-bound-flag', follows all 'value of each parameter'.

So, the ambiguity here is that the first 'if' should affect all lines up to the end while the second 'if' must affect only the next line.

Besides that, there are other places in this same documentation where 'if' blocks are bounded by curly braces and even have nested 'ifs', for example: https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::Hand... or https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnDefi....

So, this is a documentation issue that caused different implementations. Implementations that should also be fixed, now that we know this is wrong.
[11 May 2022 16:14] Philip Olson
It appears this bug was lost/forgotten over time, but it was fixed in 8.0.24 and here's the release note:

  Prepared statements without parameters would violate the MySQL
  protocol by sending unnecessary extra bytes.

Thank you Sergei for the bug report, we're sorry for the delayed response.