Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dot in database table prefix causing php 'Segmentation fault' #28307

Closed
dkuzevanov opened this issue Apr 23, 2019 · 13 comments
Closed

Dot in database table prefix causing php 'Segmentation fault' #28307

dkuzevanov opened this issue Apr 23, 2019 · 13 comments
Labels

Comments

@dkuzevanov
Copy link

  • Laravel Version: 5.8.13
  • PHP Version: 7.3.4
  • Database Driver & Version: Microsoft SQL Server 2012 - 11.0.5522.0 (X64)

Description:

Something goes wrong while using tables prefix with dot (when trying to get data from tables like sys.server_principals via model). As a result we got Segmentation fault.
The gdb showed that last call is mb_strpos:

[root@centos ums]# gdb php
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-114.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /opt/remi/php73/root/usr/bin/php...Reading symbols from /usr/lib/debug/opt/remi/php73/root/usr/bin/php.debug...done.
done.
(gdb) run artisan tinker
Starting program: /usr/bin/php artisan tinker
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Psy Shell v0.9.9 (PHP 7.3.4 — cli) by Justin Hileman
>>> App\Models\Special\User::where('name', 'sa')->first();

Program received signal SIGSEGV, Segmentation fault.
mbfl_strpos (haystack=haystack@entry=0x7fffff7ff680, needle=needle@entry=0x7fffff7ff6a0, offset=0, reverse=reverse@entry=0) at /usr/src/debug/php-7.3.4/ext/mbstring/libmbfl/mbfl/mbfilter.c:887
887                             jtbl[i] = needle_u8_len + 1;
(gdb) bt full
#0  mbfl_strpos (haystack=haystack@entry=0x7fffff7ff680, needle=needle@entry=0x7fffff7ff6a0, offset=0, reverse=reverse@entry=0) at /usr/src/debug/php-7.3.4/ext/mbstring/libmbfl/mbfl/mbfilter.c:887
        jtbl = <error reading variable jtbl (Cannot access memory at address 0x7fffff7fee10)>
        needle_u8_len = 2
        i = <optimized out>
        q = <optimized out>
        e = <optimized out>
        needle_u8_val = 0x555555c70368 "->"
        p = <optimized out>
        haystack_u8_val = 0x7fffdd690898 "sys.sys"
        result = 18446744073709551615
        _haystack_u8 = <error reading variable _haystack_u8 (Cannot access memory at address 0x7fffff7fedd0)>
        _needle_u8 = <error reading variable _needle_u8 (Cannot access memory at address 0x7fffff7fedf0)>
        haystack_u8 = 0x7fffff7ff680
        needle_u8 = 0x7fffff7ff6a0
        u8_tbl = 0x7fffe6864980 <mblen_table_utf8> '\001' <repeats 194 times>, "\002\002\002\002\002\002"...
#1  0x00007fffe677b1cd in zif_mb_strpos (execute_data=0x7fffdd655ec0, return_value=0x7fffdd655ea0) at /usr/src/debug/php-7.3.4/ext/mbstring/mbstring.c:2358
        offset = 0
        haystack = {no_language = mbfl_no_language_neutral, encoding = 0x7fffe6a8dac0 <mbfl_encoding_utf8>, val = 0x7fffdd690898 "sys.sys", len = 7}
        needle = {no_language = mbfl_no_language_neutral, encoding = 0x7fffe6a8dac0 <mbfl_encoding_utf8>, val = 0x555555c70368 "->", len = 2}
        enc_name = 0x0
        enc_name_len = 2305843009213693955
        n = <optimized out>
#2  0x00005555558cfd80 in ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_USED_HANDLER () at /usr/src/debug/php-7.3.4/Zend/zend_vm_execute.h:892
        call = <optimized out>
        fbc = <optimized out>
        ret = <optimized out>
#3  execute_ex (ex=0x7) at /usr/src/debug/php-7.3.4/Zend/zend_vm_execute.h:55481
No locals.
#4  0x0000555555834bd3 in zend_call_function (fci=fci@entry=0x7fffff7ff900, fci_cache=<optimized out>, fci_cache@entry=0x7fffff7ff8e0) at /usr/src/debug/php-7.3.4/Zend/zend_execute_API.c:756
        call_via_handler = 0
        current_opline_before_exception = 0x0
        i = <optimized out>
        call = 0x7fffdd655df0
        dummy_execute_data = {opline = 0x3, call = 0x3, return_value = 0x3, func = 0x3, This = {value = {lval = 3, dval = 1.4821969375237396e-323, counted = 0x3, str = 0x3, arr = 0x3, obj = 0x3, res = 0x3, ref = 0x3, ast = 0x3,
              zv = 0x3, ptr = 0x3, ce = 0x3, func = 0x3, ww = {w1 = 3, w2 = 0}}, u1 = {v = {type = 0 '\000', type_flags = 144 '\220', u = {call_info = 25532, extra = 25532}}, type_info = 1673302016}, u2 = {next = 3101083081,
              cache_slot = 3101083081, opline_num = 3101083081, lineno = 3101083081, num_args = 3101083081, fe_pos = 3101083081, fe_iter_idx = 3101083081, access_flags = 3101083081, property_guard = 3101083081,
              constant_flags = 3101083081, extra = 3101083081}}, prev_execute_data = 0x3, symbol_table = 0x7fffff7ff900, run_time_cache = 0x7fffff7ff8e0}
        fci_cache_local = {function_handler = 0x3, calling_scope = 0x3, called_scope = 0x3, object = 0x0}
        func = <optimized out>
#5  0x000055555576f904 in zif_array_map (execute_data=0x7fffdd6558b0, return_value=0x7fffdd655860) at /usr/src/debug/php-7.3.4/ext/standard/array.c:6225
        arrays = 0x7fffdd655910
        n_arrays = 2
        result = {value = {lval = 140736908031952, dval = 6.9533271360506483e-310, counted = 0x7fffdd68f7d0, str = 0x7fffdd68f7d0, arr = 0x7fffdd68f7d0, obj = 0x7fffdd68f7d0, res = 0x7fffdd68f7d0, ref = 0x7fffdd68f7d0,
            ast = 0x7fffdd68f7d0, zv = 0x7fffdd68f7d0, ptr = 0x7fffdd68f7d0, ce = 0x7fffdd68f7d0, func = 0x7fffdd68f7d0, ww = {w1 = 3714643920, w2 = 32767}}, u1 = {v = {type = 0 '\000', type_flags = 0 '\000', u = {call_info = 0,
                extra = 0}}, type_info = 0}, u2 = {next = 21845, cache_slot = 21845, opline_num = 21845, lineno = 21845, num_args = 21845, fe_pos = 21845, fe_iter_idx = 21845, access_flags = 21845, property_guard = 21845,
            constant_flags = 21845, extra = 21845}}
        fci = {size = 56, function_name = {value = {lval = 140736908054144, dval = 6.9533271371470788e-310, counted = 0x7fffdd694e80, str = 0x7fffdd694e80, arr = 0x7fffdd694e80, obj = 0x7fffdd694e80, res = 0x7fffdd694e80,
              ref = 0x7fffdd694e80, ast = 0x7fffdd694e80, zv = 0x7fffdd694e80, ptr = 0x7fffdd694e80, ce = 0x7fffdd694e80, func = 0x7fffdd694e80, ww = {w1 = 3714666112, w2 = 32767}}, u1 = {v = {type = 8 '\b', type_flags = 1 '\001', u = {
                  call_info = 0, extra = 0}}, type_info = 264}, u2 = {next = 0, cache_slot = 0, opline_num = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, constant_flags = 0,
              extra = 0}}, retval = 0x7fffff7ff8c0, params = 0x7fffdd690860, object = 0x7fffe1159690, no_separation = 0 '\000', param_count = 2}
        fci_cache = {function_handler = 0x7fffdd694eb8, calling_scope = 0x7fffe000cce0, called_scope = 0x7fffe000cce0, object = 0x7fffe1159690}
        i = <optimized out>
        k = 0
        maxlen = 2
#6  0x00005555558cfd80 in ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_USED_HANDLER () at /usr/src/debug/php-7.3.4/Zend/zend_vm_execute.h:892
        call = <optimized out>
        fbc = <optimized out>
        ret = <optimized out>
#7  execute_ex (ex=0x7) at /usr/src/debug/php-7.3.4/Zend/zend_vm_execute.h:55481
No locals.

By debug i found place in the code that corresponds gdb trace:

    /**
     * Determine if the given string is a JSON selector.
     *
     * @param  string  $value
     * @return bool
     */
    protected function isJsonSelector($value)
    {
        $return = Str::contains($value, '->');
        return $return;
    }

Steps To Reproduce:

  1. Make any database connection with table prefix with dot;
  2. Make any model corresponds to this connection;
  3. Try to get eny model from database via Illuminate\Database\Eloquent\Builder.
@dkuzevanov
Copy link
Author

dkuzevanov commented Apr 23, 2019

Looks like uncontrolled recursion in \Illuminate\Database\Query\Grammars\Grammar::wrap() method.

@driesvints
Copy link
Member

@staudenmeir didn't we experience this issue before?

@staudenmeir
Copy link
Contributor

@driesvints This is a different issue. It's caused by Laravel and affects all databases. I'll look into it.

@driesvints
Copy link
Member

@staudenmeir thanks!

@manan-jadhav
Copy link
Contributor

This points to a bigger problem that Laravel cannot support tables with . in their names, although it is supported by SQL Databases.

@staudenmeir
Copy link
Contributor

staudenmeir commented May 10, 2019

@CurosMJ We can't really support dots in table names because we can't differentiate between database.table and table.with.dots.

@manan-jadhav
Copy link
Contributor

@staudenmeir Yes that's true.

Also, to correct my previous comment, MySQL does not allow table and database names with . in them. @dkuzevanov Does SQL Server allow this?

@staudenmeir
Copy link
Contributor

@CurosMJ MySQL does support it if you quote the table name:

select * from `table.with.dots`

@staudenmeir
Copy link
Contributor

@driesvints I couldn't find a good solution and I suggest that we close this as "not supported".

It's an extreme edge case and not really good practice to have table names with dots. Even with a fix, only the table prefix could contain dots but not the "main" table name (#28307 (comment)).

@driesvints
Copy link
Member

Yeah, I think this is going to be impossible to fix. But we could change it in a major release if enough people would want it to be supported. Thanks for looking into it 👍

@bAngerman
Copy link

It feels somewhat hacky but I was able to pass in a table with a dot in the name by providing an \Illuminate\Database\Query\Expression as the argument.
Eg.

$table = (new Expression('`tablename.with.dots`'));
dd( DB::table($table)->count() ); // works

@BlitzCry
Copy link

BlitzCry commented Mar 12, 2024

It feels somewhat hacky but I was able to pass in a table with a dot in the name by providing an \Illuminate\Database\Query\Expression as the argument. Eg.

$table = (new Expression('`tablename.with.dots`'));
dd( DB::table($table)->count() ); // works

It might be long overdue but I second your answer.

Looks like by passing it as an expression it bypasses the wrap method
image

Which somehow ends in an infinite callback between the wrapTable and wrapSegments methods. This is all started by the compileFrom method.
image

@staudenmeir or @bAngerman
I did not have much time to investigate but I think this might be a bug, mind looking into it? I will too when I will have time.

@BlitzCry
Copy link

BlitzCry commented Mar 12, 2024

Edit: Laravel 11 looks like it supports it, lol
Sauce:

if (str_contains($table, '.')) {

Issue explanation for older versions:

I came back after exploring what is happening, it looks like it gets stuck in an infinite loop and eventually it Seg faults.

Here is the simplified flow of what is happening if your prefix has dots in it "prefix.prefix.blabla"

image

Sauce:

return $this->wrapSegments(explode('.', $value));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants