php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #76726 Segmentation fault when fetching truncated 4 bytes Unicode character
Submitted: 2018-08-09 18:17 UTC Modified: 2018-08-16 18:52 UTC
From: maxiwheat at gmail dot com Assigned: adambaratz (profile)
Status: Not a bug Package: PDO DBlib
PHP Version: 7.2.8 OS: Linux Slackware 14.1
Private report: No CVE-ID: None
 [2018-08-09 18:17 UTC] maxiwheat at gmail dot com
Description:
------------
We experienced this issue when a string was inserted in a table column of type NVARCHAR and that string ended with a 4 bytes unicode character, but the column was not large enough to contain the whole string, so the string got truncated, but instead of truncating the whole 4 bytes character it got truncated in the middle (because SQL Server stores string as USC-2, 2 bytes per character) and becoming an invalid unicode character. With SQL Server Management Studio, we can still query that column without issues, except that the character is replaced by an invalid character (�)

Test script:
---------------
$conn = new PDO('dblib:host=myhost.local;dbname=MyDB;charset=UTF-8;', 'myuser', 'mypass');

$sql = "DECLARE @test AS NVARCHAR(1) = N'????'; SELECT @test AS test;";
$stmt = $conn->prepare($sql);
$stmt->execute();

// Skip rowcount result
$stmt->nextRowset();

$row = $stmt->fetch(PDO::FETCH_ASSOC);

echo $row['test'];

Expected result:
----------------
Outputs truncated string/byte like � or an empty string, does not segfault.

Actual result:
--------------
Segmentation fault

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2018-08-09 18:21 UTC] maxiwheat at gmail dot com
a
 [2018-08-09 18:23 UTC] maxiwheat at gmail dot com
SQL query got saved badly, should be :

$sql = "DECLARE @test AS NVARCHAR(1) = N'????'; SELECT @test AS test;";
 [2018-08-09 18:27 UTC] maxiwheat at gmail dot com
Arggggg cannot even post the bug correctly,,, it does not appear correctly here.... character that produce the bug is the christmas tree : https://emojipedia.org/christmas-tree/

// ???? should be a christmas tree emoji
$sql = "DECLARE @test AS NVARCHAR(1) = N'????'; SELECT @test AS test;";
 [2018-08-13 11:01 UTC] cmb@php.net
-Status: Open +Status: Assigned -Assigned To: +Assigned To: adambaratz
 [2018-08-13 11:01 UTC] cmb@php.net
Adam, could you have a look at this issue, please?
 [2018-08-16 13:45 UTC] adambaratz@php.net
-Status: Assigned +Status: Verified
 [2018-08-16 13:45 UTC] adambaratz@php.net
Thanks for catching this. I'm able to reproduce. Will follow up when I have a fix.
 [2018-08-16 18:49 UTC] adambaratz@php.net
-Status: Verified +Status: Not a bug
 [2018-08-16 18:49 UTC] adambaratz@php.net
Dug into this some more. I believe the bug is with FreeTDS, not pdo_dblib.

Here's the stack trace I pulled out:

#0  0x00007ffff5807dcc in _IO_vfprintf_internal (s=s@entry=0x7fffffff9580,
    format=<optimized out>, format@entry=0x1410e90 "%s\377%s",
    ap=ap@entry=0x7fffffff9770) at vfprintf.c:1642
#1  0x00007ffff582f133 in _IO_vasprintf (
    result_ptr=result_ptr@entry=0x7fffffff96c8,
    format=format@entry=0x1410e90 "%s\377%s", args=args@entry=0x7fffffff9770)
    at vasprintf.c:62
#2  0x00007ffff7326b9e in tds_vstrbuild (buffer=0x14266f0 "", buflen=214,
    resultlen=0x7fffffff975c,
    text=0x7ffff7335540 "Character set conversion is not available between client character set '%1!' and server character set '%2!'", textlen=107,
    formats=<optimized out>, formatlen=-9, ap=0x7fffffff9770)
    at vstrbuild.c:106
#3  0x00007ffff72f5a8e in dbperror (dbproc=0x140d900, msgno=2401, errnum=0)
    at dblib.c:8008
#4  0x00007ffff73013c1 in _dblib_handle_err_message (tds_ctx=<optimized out>,
    tds=<optimized out>, msg=0x7fffffff98a0) at dbutil.c:135
#5  0x00007ffff7311cbf in tdserror (tds_ctx=0x1401da0, tds=0x140e1e0,
    msgno=2401, errnum=0) at util.c:351
#6  0x00007ffff732e6f0 in tds_convert_stream (tds=0x303030,
    tds@entry=0x140e1e0, char_conv=0x1410e90, direction=(unknown: 32),
    direction@entry=to_client, istream=0xffffffffffffffff,
    istream@entry=0x7fffffffa9b0, ostream=0x303030,
    ostream@entry=0x7fffffffa9d0) at stream.c:139
#7  0x00007ffff731453c in read_and_convert (outbytesleft=4,
    outbuf=0x140e960 "", wire_size=<synthetic pointer>,
    char_conv=<optimized out>, tds=0x140e1e0) at read.c:297
#8  tds_get_char_data (tds=tds@entry=0x140e1e0,
    row_buffer=row_buffer@entry=0x140e960 "", wire_size=wire_size@entry=2,
    curcol=curcol@entry=0x1426630) at read.c:231
#9  0x00007ffff7327683 in tds_generic_get (tds=0x140e1e0, curcol=0x1426630)
    at data.c:802
#10 0x00007ffff730c6a3 in tds_process_row (tds=0x140e1e0) at token.c:1937
#11 0x00007ffff7310d2d in tds_process_tokens (tds=0x140e1e0,
    result_type=0x1410e90, result_type@entry=0x7fffffffab6c, done_flags=0x20,
    done_flags@entry=0x0, flag=4294967295, flag@entry=5384) at token.c:657
#12 0x00007ffff73000f1 in dbnextrow (dbproc=0x140d900) at dblib.c:2096
#13 0x0000000000638654 in pdo_dblib_stmt_fetch (stmt=0x7ffff2c80000,
    ori=<optimized out>, offset=<optimized out>)
    at /home/vagrant/php-src/ext/pdo_dblib/dblib_stmt.c:207
#14 0x000000000063297f in do_fetch_common (stmt=stmt@entry=0x7ffff2c80000,
    ori=ori@entry=PDO_FETCH_ORI_NEXT, offset=offset@entry=0, do_bind=1)
    at /home/vagrant/php-src/ext/pdo/pdo_stmt.c:669
#15 0x0000000000634df5 in zim_PDOStatement_fetchColumn (
    execute_data=<optimized out>, return_value=0x7ffff2c1d110)
    at /home/vagrant/php-src/ext/pdo/pdo_stmt.c:1351
#16 0x000000000081e204 in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER ()
    at /home/vagrant/php-src/Zend/zend_vm_execute.h:1102
#17 execute_ex (ex=0x303030)
    at /home/vagrant/php-src/Zend/zend_vm_execute.h:55486
#18 0x000000000081f31d in zend_execute (op_array=0x7ffff2c7d2a0,
    op_array@entry=0x7ffff2c8f200,
    return_value=return_value@entry=0x7ffff2c1d030)
    at /home/vagrant/php-src/Zend/zend_vm_execute.h:60882
#19 0x0000000000792cb4 in zend_execute_scripts (type=type@entry=8,
    retval=0x7ffff2c1d030, retval@entry=0x0, file_count=file_count@entry=3)
    at /home/vagrant/php-src/Zend/zend.c:1562
#20 0x0000000000734930 in php_execute_script (primary_file=0x7fffffffd270)
    at /home/vagrant/php-src/main/main.c:2630
#21 0x000000000082166d in do_cli (argc=3158064, argv=0x1410e90)
    at /home/vagrant/php-src/sapi/cli/php_cli.c:997
#22 0x0000000000443d88 in main (argc=3158064, argv=0x1410e90)
    at /home/vagrant/php-src/sapi/cli/php_cli.c:1390

The main area of interest is in frame #2, when it tries to build an error string. I _think_ what's happening is that the argument list passed to tds_vstrbuild() is created with 0 rather than 2 elements. But here is where my comfort with this code breaks down. I'd suggest opening an issue on their repo[1]. If feedback is slow coming, you could try dropping a note on their mail list[2]. Hope this helps!

---
[1] https://github.com/FreeTDS/freetds
[2] https://lists.ibiblio.org/mailman/listinfo/freetds
 [2018-08-16 18:52 UTC] adambaratz@php.net
In case it's helpful for later, here's the start of a .phpt test:

--TEST--
PDO_DBLIB: segfault when fetching a Unicode character
--SKIPIF--
<?php
if (!extension_loaded('pdo_dblib')) die('skip not loaded');
require dirname(__FILE__) . '/config.inc';
?>
--FILE--
<?php
require dirname(__FILE__) . '/config.inc';

$stmt = $db->query("DECLARE @test AS NVARCHAR(1) = N'\u{1F384}'; SELECT @test AS test;");

var_dump($stmt->nextRowset());
var_dump($stmt->fetchColumn());
?>
--EXPECT--
 
PHP Copyright © 2001-2018 The PHP Group
All rights reserved.
Last updated: Tue Oct 16 03:01:25 2018 UTC