php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #81706 NULL pointer dereference in mysqlnd package
Submitted: 2022-01-19 01:00 UTC Modified: 2022-02-08 06:09 UTC
From: ive_jihwan at zerocution dot com Assigned:
Status: Duplicate Package: MySQLi related
PHP Version: Irrelevant OS: Ubuntu
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: ive_jihwan at zerocution dot com
New email:
PHP Version: OS:

 

 [2022-01-19 01:00 UTC] ive_jihwan at zerocution dot com
Description:
------------
Since mysqli_stmt::prepare doesn't initialize any bound results even its preparation is failed, unexpected memory behavior (NULL dereference, segfault at weird address, general protection fault, memory leak) can occur. This can cause Denial of Service surely and might be elaborated to severe security vulnerabilities.

The fault address may vary by changing the number of querying columns, or bound variables. In addition, when I build with Zend debug enabled (--enable-debug), the fault occurs near ip. I attached AddressSanitizer log with PHP 8.2-dev build (without --enable-debug)

I attached two scripts, for null dereference and memory leak. For segmentation fault, I attached AddressSanitizer log as the actual result. And I attached PHP debug build log for memory leak

- PHP 7.2.34
- PHP 7.4.27
- PHP 8.0.14
- PHP 8.1.1
- PHP 8.2.0-dev 

Test script:
---------------
<?php
// NULL Deref
$mysqli = new mysqli("127.0.0.1", "test", "test", "test");

$stmt = $mysqli->prepare("select 1,2,3");
$stmt->bind_result($a,$a,$a);
$stmt->prepare("");
$stmt->prepare("select ".str_repeat("'A',", 0x1201)."1");
?>


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

<?php
// Memory leak
$mysqli = new mysqli("127.0.0.1", "test", "test", "test");

$stmt = $mysqli->prepare("select 1,2,3");
$stmt->bind_result($a,$a,$a);
$stmt->prepare("");
$stmt->prepare("select 1");

Expected result:
----------------
Everything must be happy

Actual result:
--------------
AddressSanitizer:DEADLYSIGNAL
=================================================================
==5841==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5637c5781605 bp 0x7fff0d265110 sp 0x7fff0d2650e0 T0)
==5841==The signal is caused by a READ memory access.
==5841==Hint: address points to the zero page.
    #0 0x5637c5781604 in zend_gc_refcount /home/payload/php-src/Zend/zend_types.h:1185
    #1 0x5637c5781604 in zval_refcount_p /home/payload/php-src/Zend/zend_types.h:1234
    #2 0x5637c5781604 in mysqlnd_stmt_separate_result_bind /home/payload/php-src/ext/mysqlnd/mysqlnd_ps.c:1710
    #3 0x5637c57817ec in mysqlnd_mysqlnd_stmt_free_stmt_result_pub /home/payload/php-src/ext/mysqlnd/mysqlnd_ps.c:1737
    #4 0x5637c5781bdf in mysqlnd_mysqlnd_stmt_free_stmt_content_pub /home/payload/php-src/ext/mysqlnd/mysqlnd_ps.c:1780
    #5 0x5637c57828cd in mysqlnd_mysqlnd_stmt_close_on_server_priv /home/payload/php-src/ext/mysqlnd/mysqlnd_ps.c:1859
    #6 0x5637c5782bad in mysqlnd_mysqlnd_stmt_dtor_pub /home/payload/php-src/ext/mysqlnd/mysqlnd_ps.c:1884
    #7 0x5637c51079e7 in php_clear_stmt_bind /home/payload/php-src/ext/mysqli/mysqli.c:157
    #8 0x5637c5107fec in mysqli_stmt_free_storage /home/payload/php-src/ext/mysqli/mysqli.c:246
    #9 0x5637c5e34c15 in zend_objects_store_del /home/payload/php-src/Zend/zend_objects_API.c:200
    #10 0x5637c595b738 in rc_dtor_func /home/payload/php-src/Zend/zend_variables.c:57
    #11 0x5637c595bb51 in i_zval_ptr_dtor /home/payload/php-src/Zend/zend_variables.h:44
    #12 0x5637c595bb51 in zval_ptr_dtor /home/payload/php-src/Zend/zend_variables.c:84
    #13 0x5637c59de270 in _zend_hash_del_el_ex /home/payload/php-src/Zend/zend_hash.c:1411
    #14 0x5637c59de270 in _zend_hash_del_el /home/payload/php-src/Zend/zend_hash.c:1438
    #15 0x5637c59de270 in zend_hash_reverse_apply /home/payload/php-src/Zend/zend_hash.c:2156
    #16 0x5637c590173d in shutdown_destructors /home/payload/php-src/Zend/zend_execute_API.c:252
    #17 0x5637c5968c71 in zend_call_destructors /home/payload/php-src/Zend/zend.c:1225
    #18 0x5637c57940a1 in php_request_shutdown /home/payload/php-src/main/main.c:1808
    #19 0x5637c60335b1 in do_cli /home/payload/php-src/sapi/cli/php_cli.c:1135
    #20 0x5637c60345f8 in main /home/payload/php-src/sapi/cli/php_cli.c:1367
    #21 0x7fc53c01a0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
    #22 0x5637c4e02e8d in _start (/home/payload/php82_asan/bin/php+0x402e8d)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/payload/php-src/Zend/zend_types.h:1185 in zend_gc_refcount
==5841==ABORTING


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


[Wed Jan 19 10:00:20 2022]  Script:  '/tmp/poc2.php'
/home/payload/php-src/ext/mysqlnd/mysqlnd_alloc.h(93) :  Freeing 0x00007f27c0401050 (5 bytes), script=/tmp/poc2.php
/home/payload/php-src/ext/mysqlnd/mysqlnd_alloc.c(304) : Actual location (location was relayed)
Last leak repeated 4 times
[Wed Jan 19 10:00:20 2022]  Script:  '/tmp/poc2.php'
/home/payload/php-src/Zend/zend_string.h(150) :  Freeing 0x00007f27c0402c80 (40 bytes), script=/tmp/poc2.php
Last leak repeated 3 times
[Wed Jan 19 10:00:20 2022]  Script:  '/tmp/poc2.php'
/home/payload/php-src/ext/mysqlnd/mysqlnd_statistics.c(217) :  Freeing 0x00007f27c04030c0 (16 bytes), script=/tmp/poc2.php
[Wed Jan 19 10:00:20 2022]  Script:  '/tmp/poc2.php'
/home/payload/php-src/Zend/zend_vm_execute.h(47621) :  Freeing 0x00007f27c045a6c0 (32 bytes), script=/tmp/poc2.php
[Wed Jan 19 10:00:20 2022]  Script:  '/tmp/poc2.php'
/home/payload/php-src/ext/mysqlnd/mysqlnd_connection.c(1515) :  Freeing 0x00007f27c045a700 (30 bytes), script=/tmp/poc2.php
/home/payload/php-src/ext/mysqlnd/mysqlnd_alloc.c(349) : Actual location (location was relayed)
[Wed Jan 19 10:00:20 2022]  Script:  '/tmp/poc2.php'
/home/payload/php-src/ext/mysqlnd/mysqlnd_connection.c(681) :  Freeing 0x00007f27c045a740 (29 bytes), script=/tmp/poc2.php
/home/payload/php-src/ext/mysqlnd/mysqlnd_alloc.c(349) : Actual location (location was relayed)
[Wed Jan 19 10:00:20 2022]  Script:  '/tmp/poc2.php'
/home/payload/php-src/ext/mysqlnd/mysqlnd_connection.c(1573) :  Freeing 0x00007f27c045b480 (56 bytes), script=/tmp/poc2.php
/home/payload/php-src/ext/mysqlnd/mysqlnd_alloc.c(94) : Actual location (location was relayed)
[Wed Jan 19 10:00:20 2022]  Script:  '/tmp/poc2.php'
/home/payload/php-src/ext/mysqlnd/mysqlnd_commands.c(641) :  Freeing 0x00007f27c045b4e0 (53 bytes), script=/tmp/poc2.php
/home/payload/php-src/ext/mysqlnd/mysqlnd_alloc.c(349) : Actual location (location was relayed)
[Wed Jan 19 10:00:20 2022]  Script:  '/tmp/poc2.php'
/home/payload/php-src/ext/mysqlnd/mysqlnd_auth.c(104) :  Freeing 0x00007f27c04601f8 (21 bytes), script=/tmp/poc2.php
/home/payload/php-src/ext/mysqlnd/mysqlnd_alloc.c(94) : Actual location (location was relayed)
[Wed Jan 19 10:00:20 2022]  Script:  '/tmp/poc2.php'
/home/payload/php-src/ext/mysqlnd/mysqlnd_driver.c(246) :  Freeing 0x00007f27c046c480 (344 bytes), script=/tmp/poc2.php
/home/payload/php-src/ext/mysqlnd/mysqlnd_alloc.c(136) : Actual location (location was relayed)
[Wed Jan 19 10:00:20 2022]  Script:  '/tmp/poc2.php'
/home/payload/php-src/Zend/zend_hash.c(172) :  Freeing 0x00007f27c046c600 (320 bytes), script=/tmp/poc2.php
[Wed Jan 19 10:00:20 2022]  Script:  '/tmp/poc2.php'
/home/payload/php-src/ext/mysqlnd/mysqlnd_statistics.c(218) :  Freeing 0x00007f27c0479600 (1304 bytes), script=/tmp/poc2.php
[Wed Jan 19 10:00:20 2022]  Script:  '/tmp/poc2.php'
/home/payload/php-src/ext/mysqlnd/mysqlnd_driver.c(267) :  Freeing 0x00007f27c0482400 (216 bytes), script=/tmp/poc2.php
/home/payload/php-src/ext/mysqlnd/mysqlnd_alloc.c(136) : Actual location (location was relayed)
[Wed Jan 19 10:00:20 2022]  Script:  '/tmp/poc2.php'
/home/payload/php-src/ext/mysqlnd/mysqlnd_driver.c(224) :  Freeing 0x00007f27c048d140 (240 bytes), script=/tmp/poc2.php
/home/payload/php-src/ext/mysqlnd/mysqlnd_alloc.c(136) : Actual location (location was relayed)
[Wed Jan 19 10:00:20 2022]  Script:  '/tmp/poc2.php'
/home/payload/php-src/ext/mysqlnd/mysqlnd_driver.c(110) :  Freeing 0x00007f27c049c500 (1208 bytes), script=/tmp/poc2.php
/home/payload/php-src/ext/mysqlnd/mysqlnd_alloc.c(136) : Actual location (location was relayed)
=== Total 22 memory leaks detected ===


P.S. Since I add some debug output at mysqlnd_ps.c, the detailed line number can be different slightly.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2022-01-19 07:18 UTC] stas@php.net
-Type: Security +Type: Bug
 [2022-01-19 07:42 UTC] ive_jihwan at zerocution dot com
For more convenience, this bug can be triggered more generally.

$stmt = $mysqli->prepare("select 1"); // actually not big matter at here how many columns are selected here
$stmt->bind_result($a);
$stmt->prepare("select a where haha=lol"); // it's okay for any wrong query
$stmt->prepare("select 1".str_repeat(',1', 1000));
 [2022-01-28 02:29 UTC] ive_jihwan at zerocution dot com
Any updates?
 [2022-02-08 06:09 UTC] requinix@php.net
-Status: Open +Status: Duplicate
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Apr 18 17:01:28 2024 UTC