Bug #80713 [Ver->Csd]: SegFault when disabling ATTR_EMULATE_PREPARES and MySQL 8.0

From: Date: Tue, 16 Feb 2021 14:36:34 +0000
Subject: Bug #80713 [Ver->Csd]: SegFault when disabling ATTR_EMULATE_PREPARES and MySQL 8.0
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-232200@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=80713&edit=1

 ID:                 80713
 Updated by:         nikic@php.net
 Reported by:        php at attrib dot org
 Summary:            SegFault when disabling ATTR_EMULATE_PREPARES and
                     MySQL 8.0
-Status:             Verified
+Status:             Closed
 Type:               Bug
 Package:            PDO MySQL
 Operating System:   Debian Buster
 PHP Version:        7.4.15
-Assigned To:        
+Assigned To:        nikic
 Block user comment: N
 Private report:     N

 New Comment:

I've committed https://github.com/php/php-src/commit/9552cf6b84b73de38c152b97cc8d30ac3509d4a5
to handle this case more gracefully on our side (we shouldn't crash even if we get an incorrect
response...)

I've also tested what happens when libmysqlclient is used instead of mysqlnd, and in that case
the script hangs.


Previous Comments:
------------------------------------------------------------------------
[2021-02-16 14:19:50] nikic@php.net

Wanted to report this upstream, but apparently you need to fill out https://profile.oracle.com/myprofile/account/create-account.jspx
for that, which is a no-go for obvious reasons.

Here is the wireshark dump of the problematic server response:

Frame 16: 152 bytes on wire (1216 bits), 152 bytes captured (1216 bits) on interface lo, id 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 3306, Dst Port: 42692, Seq: 155, Ack: 188, Len: 86
MySQL Protocol
    Packet Length: 12
    Packet Number: 1
    Response Code: OK Packet (0x00)
    Statement ID: 1
    Number of fields: 1
    Number of parameter: 1
    Warnings: 0
MySQL Protocol
    Packet Length: 5
    Packet Number: 2
    Response Code: EOF Packet (0xfe)
    EOF marker: 254
    Warnings: 0
    Server Status: 0x0002
MySQL Protocol
    Packet Length: 48
    Packet Number: 3
    Catalog: def
    Database: test
    Table: test
    Original table: test
    Name: updated
    Original name: updated
    Charset number: binary COLLATE binary (63)
    Length: 10
    Type: FIELD_TYPE_DATE (10)
    Flags: 0x0080
    Decimals: 0
MySQL Protocol
    Packet Length: 5
    Packet Number: 4
    Response Code: EOF Packet (0xfe)
    EOF marker: 254
    Warnings: 0
    Server Status: 0x0002

------------------------------------------------------------------------
[2021-02-16 14:11:26] nikic@php.net

A bit reduced:

$pdo = new PDO($dsn, 'root', '', [ 
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_EMULATE_PREPARES => false,
]);

//$pdo->exec('CREATE TABLE test(updated DATE)');

$stmt = $pdo->prepare("SELECT * FROM test ORDER BY :orderBy DESC");
$stmt->execute(['orderBy' => 'updated']);
var_dump($stmt->fetchAll(PDO::FETCH_ASSOC));

So, obviously the query is invalid (you're trying to prepare an identifier, not a value) and
should fail.

This looks like a bug on the MySQL server side, because the server responds with COM_STMT_PREPARE_OK
with num_columns=1 and num_params=1, but then directly follows with an EOF in the parameter
definition block. The following column definition block is correct.

------------------------------------------------------------------------
[2021-02-05 12:58:01] php at attrib dot org

Forgot to mention, it works if I use mysql:5.7. Only with mysql:8.0

------------------------------------------------------------------------
[2021-02-05 12:56:12] php at attrib dot org

Description:
------------
Disabling ATTR_EMULATE_PREPARES produces a segmentation fault when using MySQL 8.0.

See test script.

Test setup:

Running a docker container for mysql (
docker run --rm -it -e MYSQL_ROOT_PASSWORD="test" -e MYSQL_DATABASE="systemA"
mysql:8)

With a empty test table (at least id, updated field)

Running the script in a second container using php:7.4.15-cli or php:8.0.2-cli docker image for PHP
and running "docker-php-ext-install mysqli pdo_mysql".

Then call the script and the SegFault happens.

gbd output:

(gdb) run mysql.php
Starting program: /usr/local/bin/php mysql.php
warning: Error disabling address space randomization: Operation not permitted
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x0000555a8da07eb8 in ?? ()
(gdb) bt
#0  0x0000555a8da07eb8 in ?? ()
#1  0x0000555a8da04429 in ?? ()
#2  0x0000555a8da0e4ff in ?? ()
#3  0x00007f0d5b3f91cc in mysql_handle_preparer ()
   from /usr/local/lib/php/extensions/no-debug-non-zts-20190902/pdo_mysql.so
#4  0x0000555a8d8f38b5 in ?? ()
#5  0x00007f0d5b6514a5 in xdebug_execute_internal (current_execute_data=0x7f0d5b413160, 
    return_value=0x7f0d5b4130d0) at /tmp/pear/temp/xdebug/src/base/base.c:921
#6  0x0000555a8daf853c in execute_ex ()
#7  0x00007f0d5b650ae1 in xdebug_execute_ex (execute_data=0x7f0d5b413020)
    at /tmp/pear/temp/xdebug/src/base/base.c:803
#8  0x0000555a8daf9073 in zend_execute ()
#9  0x0000555a8da731a3 in zend_execute_scripts ()
#10 0x0000555a8da12dd0 in php_execute_script ()
#11 0x0000555a8dafb156 in ?? ()
#12 0x0000555a8d7acb4b in ?? ()
#13 0x00007f0d5e4b909b in __libc_start_main (main=0x555a8d7ac700, argc=2, argv=0x7ffeedecde78, 
    init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>,
stack_end=0x7ffeedecde68)
    at ../csu/libc-start.c:308
#14 0x0000555a8d7ad28a in _start ()
(gdb) 


Test script:
---------------
<?php
$pdo = new PDO(sprintf('mysql:dbname=%s;host=%s;charset=utf8', 'systemA',
'172.17.0.4:3306'), 'root', 'test', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_TIMEOUT => 30,
    PDO::ATTR_EMULATE_PREPARES => false,
]);

$resultAssoc = $pdo->prepare("SELECT * FROM test WHERE :field >= :date ORDER BY :orderBy
DESC LIMIT :offset, :limit");
$resultAssoc->execute([
    'field' => 'updated',
    'date' => (new DateTime('2020-02-01'))->format('Y-m-d
H:i:s'),
    'orderBy' => 'id',
    'limit' => 1000,
    'offset' => 0,
]);
var_dump($resultAssoc->fetchAll(PDO::FETCH_ASSOC));


Expected result:
----------------
Empty array if nothing is in the DB.

Actual result:
--------------
Segmentation fault (core dumped)


------------------------------------------------------------------------



--
Edit this bug report at https://bugs.php.net/bug.php?id=80713&edit=1


Thread (6 messages)

« previous php.bugs (#232200) next »