Bug #29494 Field Packet with NULL fields crashes libmysqlclient
Submitted: 2 Jul 2007 18:47 Modified: 27 Jul 2007 16:02
Reporter: Jan Kneschke Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: C API (client library) Severity:S3 (Non-critical)
Version:5.1.19 OS:Any
Assigned to: Alexey Botchkov CPU Architecture:Any

[2 Jul 2007 18:47] Jan Kneschke
Description:
While developing the MySQL Proxy I stumbled over a crash in the mysql-client library.

According 

http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Field_Packet

all fields in the Field Packet are Length Encoded, but it is not defined how to handle undefined fields. For a query like "SELECT 1" no DB is set, nor any original values.

Sending (SQL) NULL (octal \373, 0xfb, 251) results in a crash of the mysql client:

##  Result Set Header Packet
write(9, "\1\0\0\1\1", 5)               = 5
##  Field Packet
write(9, "\32\0\0\2\3def\373\0\0\4rows\0\f\10\0 \0\0\0\3\2\0\0\0\0", 30) = 30
## EOF Packet
write(9, "\5\0\0\3\376\0\0\2\0", 9)     = 9
...

Sending a "" seems to be expected, but not enforced.

How to repeat:
Use MySQL proxy revision 51, apply this patch:

Index: src/network-mysqld.c
===================================================================
--- src/network-mysqld.c        (revision 51)
+++ src/network-mysqld.c        (working copy)
@@ -1548,7 +1548,7 @@
                g_string_truncate(s, 0);

                g_string_lenenc_append(s, field->catalog ? field->catalog : "def");   /* catalog */
-               g_string_lenenc_append(s, field->db ? field->db : "");                /* database */
+               g_string_lenenc_append(s, field->db ? field->db : NULL);                /* database */
                g_string_lenenc_append(s, field->table ? field->table : "");          /* table */
                g_string_lenenc_append(s, field->org_table ? field->org_table : "");  /* org_table */
                g_string_lenenc_append(s, field->name ? field->name : "");            /* name */

and run the test-suite.

==10946== Invalid read of size 1
==10946==    at 0x4A21402: strlen (in /usr/lib64/valgrind/amd64-linux/vgpreload_memcheck.so)
==10946==    by 0x42AA7B: strdup_root (in /usr/bin/mysqltest)
==10946==    by 0x44309A: unpack_fields (in /usr/bin/mysqltest)
==10946==    by 0x44509A: (within /usr/bin/mysqltest)
==10946==    by 0x427B8C: mysql_read_query_result (in /usr/bin/mysqltest)
==10946==    by 0x418603: run_query_normal (in /usr/bin/mysqltest)
==10946==    by 0x41906D: run_query (in /usr/bin/mysqltest)
==10946==    by 0x419EFE: main (in /usr/bin/mysqltest)
==10946==  Address 0x0 is not stack'd, malloc'd or (recently) free'd

Keep in mind that all empty fields are affected (db, org_db, table, org_table, ...)

Suggested fix:
Check that the field-len is valid and close the connection if not.

This can be exploited by writing a fake-server (like the proxy) and used to DoS all applications which use libmysqlclient. As mysql runs on unpriviliged ports no special permissions are needed to start the fake-server.
[3 Jul 2007 12:32] Sveta Smirnova
Thank you for the report.

Verified as described.
[6 Jul 2007 11:27] Jan Kneschke
A testcase has been added to the mysql-proxy SVN:

$ cd mysql-proxy/trunk/
$ lua tests/run-tests.lua tests/t/bug-29494.test
mysql-test exit-code: 11

The function does:

function read_query(packet)
        if packet:byte() == proxy.COM_QUERY then
                local q = packet:sub(2)

                if q == "SELECT 1 /* BUG #29494 */" then
                        -- create a packet which is will break the client
                        --
                        -- HINT: lua uses \ddd (3 decimal digits) instead of octals
                        proxy.response.type = proxy.MYSQLD_PACKET_RAW
                        proxy.response.packets = {
                                "\001",  -- one field
                                "\003def" ..   -- catalog
                                  "\251" ..  -- db, NULL (crashes client)
                                  "\0" ..    -- table
                                  "\0" ..    -- orig-table
                                  "\0011" .. -- name
                                  "\0" ..    -- orig-name
                                  "\f" ..    -- filler
                                  "\008\0" .. -- charset
                                  " \0\0\0" .. -- length
                                  "\003" ..    -- type
                                  "\002\0" ..  -- flags
                                  "\0" ..    -- decimals
                                  "\0\0",    -- filler

                                "\254\0\0\002\0", -- EOF
                                "\254\0\0\002\0"  -- no data EOF
                        }

                        return proxy.PROXY_SEND_RESULT
                end
        end
end
[20 Jul 2007 11:43] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/31227

ChangeSet@1.2676, 2007-07-20 15:42:16+05:00, holyfoot@mysql.com +1 -0
  Bug #29494 Field packet with NULL fields crashes libmysqlclient.
  
  unpack_fields() didn't expect NULL_LENGHT in the field's descriptions.
  In this case we get NULL in the resulting string so cannot use
  strdup_root to make a copy of it.
  strdup_root changed with strmake_root as it's NULL-safe
[20 Jul 2007 11:48] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/31228

ChangeSet@1.2676, 2007-07-20 15:47:50+05:00, holyfoot@mysql.com +1 -0
  Bug #29494 Field packet with NULL fields crashes libmysqlclient.
  
  unpack_fields() didn't expect NULL_LENGHT in the field's descriptions.
  In this case we get NULL in the resulting string so cannot use
  strdup_root to make a copy of it.
  strdup_root changed with strmake_root as it's NULL-safe
[20 Jul 2007 12:06] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/31233

ChangeSet@1.2676, 2007-07-20 16:05:55+05:00, holyfoot@mysql.com +1 -0
  Bug #29494 Field packet with NULL fields crashes libmysqlclient.
  
  unpack_fields() didn't expect NULL_LENGHT in the field's descriptions.
  In this case we get NULL in the resulting string so cannot use
  strdup_root to make a copy of it.
  strdup_root changed with strmake_root as it's NULL-safe
[26 Jul 2007 5:55] Bugs System
Pushed into 5.1.21-beta
[26 Jul 2007 5:56] Bugs System
Pushed into 5.0.48
[26 Jul 2007 5:57] Bugs System
Pushed into 4.1.24
[27 Jul 2007 16:02] Paul DuBois
Noted in 4.1.24, 5.0.48, 5.1.21 changelogs.

A field packet with NULL fields caused a libmysqlclient crash.