Bug #91974 mysql-connector-python crashes on 0 time value
Submitted: 11 Aug 2018 0:13 Modified: 13 Aug 2018 16:15
Reporter: Robert Walzer Email Updates:
Status: Verified Impact on me:
None 
Category:Connector / Python Severity:S3 (Non-critical)
Version:8.0.12 OS:Any
Assigned to: CPU Architecture:Any

[11 Aug 2018 0:13] Robert Walzer
Description:
The mysql-python-connector binary protocol appears to crash on time value of 0 because it does not deal with payload of size 0 correctly. According to the documentation(https://dev.mysql.com/doc/internals/en/binary-protocol-value.html) time of 00:00.0 can be compressed to size 0 in the packet. The connector yields this crash when reading this value though:

```
Traceback (most recent call last):
  File "script_tests/binary.py", line 14, in <module>
    q("select * from db.t")
  File "script_tests/binary.py", line 7, in q
    for r in c:
  File "/usr/local/lib/python2.7/dist-packages/mysql/connector/cursor.py", line 1249, in fetchone
    return self._fetch_row() or None
  File "/usr/local/lib/python2.7/dist-packages/mysql/connector/cursor.py", line 851, in _fetch_row
    binary=self._binary, columns=self.description, raw=raw)
  File "/usr/local/lib/python2.7/dist-packages/mysql/connector/connection.py", line 465, in get_row
    raw=raw)
  File "/usr/local/lib/python2.7/dist-packages/mysql/connector/connection.py", line 491, in get_rows
    self._socket, columns, count, charset)
  File "/usr/local/lib/python2.7/dist-packages/mysql/connector/protocol.py", line 478, in read_binary_result
    values = self._parse_binary_values(columns, packet[5:], charset)
  File "/usr/local/lib/python2.7/dist-packages/mysql/connector/protocol.py", line 450, in _parse_binary_values
    (packet, value) = self._parse_binary_time(packet, field)
  File "/usr/local/lib/python2.7/dist-packages/mysql/connector/protocol.py", line 415, in _parse_binary_time
    days = struct_unpack('I', data[1:5])[0]
  File "/usr/local/lib/python2.7/dist-packages/mysql/connector/catch23.py", line 91, in struct_unpack
    return struct.unpack_from(fmt, buffer(buf))
struct.error: unpack_from requires a buffer of at least 4 bytes
```

How to repeat:
```
import mysql.connector
from mysql.connector.cursor import MySQLCursorPrepared

def q(query):
    c = cnx.cursor(cursor_class=MySQLCursorPrepared)
    c.execute(query)
    for r in c:
        print r

cnx = mysql.connector.connect(user='root', database='',port=3306,use_pure=True)
q("create database db")
q("create table db.t(a time)")
q("insert into db.t values(0)")
q("select * from db.t")
```
[12 Aug 2018 8:43] MySQL Verification Team
Hello Robert,

Thank you for the report and test case!

thanks,
Umesh
[13 Aug 2018 16:15] Robert Walzer
I have another test case which causes the connector to crash on a binary column. Would you like me to file a separate bug for this:

  File "script_tests/binary.py", line 14, in <module>
    q("select * from db.t")
  File "script_tests/binary.py", line 7, in q
    for r in c:
  File "/usr/local/lib/python2.7/dist-packages/mysql/connector/cursor.py", line 1249, in fetchone
    return self._fetch_row() or None
  File "/usr/local/lib/python2.7/dist-packages/mysql/connector/cursor.py", line 851, in _fetch_row
    binary=self._binary, columns=self.description, raw=raw)
  File "/usr/local/lib/python2.7/dist-packages/mysql/connector/connection.py", line 465, in get_row
    raw=raw)
  File "/usr/local/lib/python2.7/dist-packages/mysql/connector/connection.py", line 491, in get_rows
    self._socket, columns, count, charset)
  File "/usr/local/lib/python2.7/dist-packages/mysql/connector/protocol.py", line 478, in read_binary_result
    values = self._parse_binary_values(columns, packet[5:], charset)
  File "/usr/local/lib/python2.7/dist-packages/mysql/connector/protocol.py", line 454, in _parse_binary_values
    values.append(value.decode(charset))
  File "/usr/lib/python2.7/encodings/utf_8.py", line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf8' codec can't decode byte 0xd0 in position 2: invalid continuation byte

repro:
def q(query):
    c = cnx.cursor(cursor_class=MySQLCursorPrepared)
    c.execute(query)
    for r in c:
        print r

cnx = mysql.connector.connect(user='root', database='',port=3306,use_pure=True)
q("create database db")
q("create table db.t(a binary(16))")
q("insert into db.t values(unhex('D0BFD000000000000000000000000000'))")
q("select * from db.t")