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

Segfault in stripslashes() with arm64 #10187

Closed
todeveni opened this issue Dec 30, 2022 · 8 comments
Closed

Segfault in stripslashes() with arm64 #10187

todeveni opened this issue Dec 30, 2022 · 8 comments

Comments

@todeveni
Copy link
Contributor

Description

The following code:

<?php
var_dump(stripslashes("1234567890abcde\\"));

Resulted in this output:

Segmentation fault (core dumped)

But I expected this output instead:

string(15) "1234567890abcde"

Backtrace from current php-src:

Starting program: /home/ubuntu/php-src/sapi/cli/php -a
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/aarch64-linux-gnu/libthread_db.so.1".

Program received signal SIGSEGV, Segmentation fault.
0x0000aaaaaaf55e40 in vld1q_u8 (__a=0xfffff57ffff9 "") at /usr/lib/gcc/aarch64-linux-gnu/11/include/arm_neon.h:16132
16132       __builtin_aarch64_ld1v16qi ((const __builtin_aarch64_simd_qi *) __a);
#0  0x0000aaaaaaf55e40 in vld1q_u8 (__a=0xfffff57ffff9 "")
    at /usr/lib/gcc/aarch64-linux-gnu/11/include/arm_neon.h:16132
No locals.
#1  php_stripslashes_impl (str=0xfffff57ffff9 "", out=0xfffff57ffff3 "", len=18446744073707838319)
    at /home/ubuntu/php-src/ext/standard/string.c:3820
        x = {0 <repeats 16 times>}
        q = {mem = '\000' <repeats 15 times>, dw = {0, 0}}
#2  0x0000aaaaaaf560f8 in php_stripslashes (str=0xfffff565db40) at /home/ubuntu/php-src/ext/standard/string.c:3936
        t = 0xfffff565db40 "\001"
#3  0x0000aaaaaaf54fb8 in zif_stripslashes (execute_data=0xfffff5613080, return_value=0xffffffffd7d8)
    at /home/ubuntu/php-src/ext/standard/string.c:3346
        str = 0xfffff565dbe0
        __PRETTY_FUNCTION__ = "zif_stripslashes"
#4  0x0000aaaaab0cb298 in ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER ()
    at /home/ubuntu/php-src/Zend/zend_vm_execute.h:1250
        call = 0xfffff5613080
        fbc = 0xaaaaabd2e5f0
        ret = 0xffffffffd7d8
        retval = {value = {lval = 281474798836544, dval = 1.3906702827521206e-309, counted = 0xfffff565db40,
            str = 0xfffff565db40, arr = 0xfffff565db40, obj = 0xfffff565db40, res = 0xfffff565db40,
            ref = 0xfffff565db40, ast = 0xfffff565db40, zv = 0xfffff565db40, ptr = 0xfffff565db40,
            ce = 0xfffff565db40, func = 0xfffff565db40, ww = {w1 = 4117093184, w2 = 65535}}, u1 = {type_info = 262,
            v = {type = 6 '\006', type_flags = 1 '\001', u = {extra = 0}}}, u2 = {next = 65535, cache_slot = 65535,
            opline_num = 65535, lineno = 65535, num_args = 65535, fe_pos = 65535, fe_iter_idx = 65535,
            property_guard = 65535, constant_flags = 65535, extra = 65535}}
        should_throw = false
        __PRETTY_FUNCTION__ = "ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER"
        call_info = 6
#5  0x0000aaaaab153a98 in execute_ex (ex=0xfffff5613020) at /home/ubuntu/php-src/Zend/zend_vm_execute.h:56013
        vm_stack_data = {orig_opline = 0x0, orig_execute_data = 0xaaaaabb9e918}
        __PRETTY_FUNCTION__ = "execute_ex"
#6  0x0000aaaaab158250 in zend_execute (op_array=0xfffff5689000, return_value=0xffffffffdaa0)
    at /home/ubuntu/php-src/Zend/zend_vm_execute.h:60381
        execute_data = 0xfffff5613020
        object_or_called_scope = 0x0
        call_info = 1245184
#7  0x0000aaaaab068f10 in zend_eval_stringl (str=0xfffff567c000 "pslashes(\"1234567890abcde\\\");\n", str_len=35,
    retval_ptr=0x0, string_name=0xaaaaaba7ac40 "php shell code") at /home/ubuntu/php-src/Zend/zend_execute_API.c:1287
        __orig_bailout = 0xffffffffdcb0
        __bailout = {{__jmpbuf = {281474976707768, 2, 187650002250008, 281474842484800, 187649992307172,
              281474837557248, 0, 281474976707792, 187650002250008, 0, 281474976700976, 12975520107903850929,
              187650002250008, 12975463846716460405, 0, 0, 0, 0, 0, 0, 0, 0}, __mask_was_saved = 0, __saved_mask = {
              __val = {2049, 543139, 4295000448, 4299262264297, 0, 0, 1191, 4096, 8, 1672388012, 315971238,
                1672387996, 675971238, 281474976701424, 187649988072524, 281474976707768}}}}
        local_retval = {value = {lval = -1148435428713435121, dval = -6.1979905163444638e+231,
            counted = 0xf00ff00ff00ff00f, str = 0xf00ff00ff00ff00f, arr = 0xf00ff00ff00ff00f,
            obj = 0xf00ff00ff00ff00f, res = 0xf00ff00ff00ff00f, ref = 0xf00ff00ff00ff00f, ast = 0xf00ff00ff00ff00f,
            zv = 0xf00ff00ff00ff00f, ptr = 0xf00ff00ff00ff00f, ce = 0xf00ff00ff00ff00f, func = 0xf00ff00ff00ff00f,
            ww = {w1 = 4027576335, w2 = 4027576335}}, u1 = {type_info = 0, v = {type = 0 '\000',
              type_flags = 0 '\000', u = {extra = 0}}}, u2 = {next = 1326149266, cache_slot = 1326149266,
            opline_num = 1326149266, lineno = 1326149266, num_args = 1326149266, fe_pos = 1326149266,
            fe_iter_idx = 1326149266, property_guard = 1326149266, constant_flags = 1326149266, extra = 1326149266}}
        new_op_array = 0xfffff5689000
        original_compiler_options = 4
        retval = SUCCESS
        code_str = 0xfffff56593c0
#8  0x0000aaaaaae1946c in readline_shell_run () at /home/ubuntu/php-src/ext/readline/readline_cli.c:700
        __orig_bailout = 0xffffffffe050
        __bailout = {{__jmpbuf = {281474976707768, 2, 187650002250008, 281474842484800, 187649992307172,
              281474837557248, 0, 281474976707792, 187650002250008, 0, 281474976701424, 12975520107884905225,
              187650001338216, 12975463846716460213, 0, 0, 0, 0, 0, 0, 0, 0}, __mask_was_saved = 0, __saved_mask = {
              __val = {187649989951672, 187650003402288, 187650003239016, 281474976701888, 187649990352168,
                281474976701888, 187650003402288, 281474798822048, 281474798822016, 4294958576, 187650004630912,
                281474976701936, 187649992304688, 281474976707768, 187650003402288, 281474798833744}}}}
        line = 0xaaaaabe746d0 "d\362M\001\240\252"
        size = 4096
        pos = 35
        len = 34
        code = 0xfffff567c000 "pslashes(\"1234567890abcde\\\");\n"
        prompt = 0xfffff567e000
        history_file = 0xaaaaabe5af20 "/home/ubuntu/.php_history"
        history_lines_to_write = 0
#9  0x0000aaaaab222944 in do_cli (argc=2, argv=0xaaaaabca9aa0) at /home/ubuntu/php-src/sapi/cli/php_cli.c:962
        __orig_bailout = 0xfffffffff200
        __bailout = {{__jmpbuf = {281474976707768, 2, 187650002250008, 281474842484800, 187649992307172,
              281474837557248, 0, 281474976707792, 187650002250008, 0, 281474976701936, 12975520107905714537,
              281474976702832, 12975463846716459701, 0, 0, 0, 0, 0, 0, 0, 0}, __mask_was_saved = 0, __saved_mask = {
              __val = {0, 187647121162261, 281474837564176, 281474976702832, 5, 281474976702960, 281474836453380,
                281474837564176, 1280, 281474842219968, 88, 281474842220056, 281474837557248, 0, 281474976707792,
                187650002250008}}}}
        c = -1
        file_handle = {handle = {fp = 0xffffffffdfe0, stream = {handle = 0xffffffffdfe0, isatty = -1425433344,
              reader = 0xaaaaabb9e918, fsizer = 0x88, closer = 0xffff00000004}}, filename = 0x0,
          opened_path = 0xffffffffe010, type = 88 'X', primary_script = 210, in_list = 9,
          buf = 0x31ffffe0b0 <error: Cannot access memory at address 0x31ffffe0b0>, len = 187650004703440}
        behavior = 1
        reflection_what = 0x0
        request_started = 1
        php_optarg = 0x0
        orig_optarg = 0x0
        php_optind = 2
        orig_optind = 1
        exec_direct = 0x0
        exec_run = 0x0
        exec_begin = 0x0
        exec_end = 0x0
        arg_free = 0xaaaaabca9af0 "-a"
        arg_excp = 0xaaaaabca9aa8
        script_file = 0x0
        translated_path = 0x0
        interactive = true
        param_error = 0x0
        hide_argv = false
        num_repeats = 1
        pid = 769201
#10 0x0000aaaaab2235a4 in main (argc=2, argv=0xaaaaabca9aa0) at /home/ubuntu/php-src/sapi/cli/php_cli.c:1333
        __orig_bailout = 0x0
        __bailout = {{__jmpbuf = {281474976707768, 2, 187650002250008, 281474842484800, 187649992307172,
              281474837557248, 0, 281474976707792, 187650002250008, 0, 281474976706960, 12975520107905720889, 0,
              12975463846716466901, 0, 0, 0, 0, 0, 0, 0, 0}, __mask_was_saved = 0, __saved_mask = {__val = {
                281474842241056, 281474842487688, 2, 281474976707768, 281474976707792, 281474803275592, 0,
                281474976707664, 281474842313132, 281474976707768, 2, 187650002250008, 281474842484800,
                281474976707392, 281474836034496, 281474976707768}}}}
        c = -1
        exit_status = 0
        module_started = 1
        sapi_started = 1
        php_optarg = 0x0
        php_optind = 2
        use_extended_info = 0
        ini_path_override = 0x0
        ini_builder = {
          value = 0xaaaaabca9d50 "html_errors=0\nregister_argc_argv=1\nimplicit_flush=1\noutput_buffering=0\nmax_execution_time=0\nmax_input_time=-1\n", length = 110}
        ini_ignore = 0
        sapi_module = 0xaaaaabc78f80 <cli_sapi_module>

Downstream bugreport oerdnj/deb.sury.org#1894 reported originally by @martymcguire

PHP Version

Any

Operating System

No response

@devnexen
Copy link
Member

can reproduce even on mac arm64

@cmb69
Copy link
Contributor

cmb69 commented Dec 30, 2022

if (s == '0')

looks fishy. Shouldn't that be if (s == '\0') instead? In which case the whole if-else could be simplified.

But the actual issue might be len underflowing. Can't check, because I don't have an ARM64 machine available.

@devnexen
Copy link
Member

devnexen commented Dec 30, 2022

if (s == '0')

But the actual issue might be len underflowing. Can't check, because I don't have an ARM64 machine available.

Looks like it

p len
(size_t) $1 = 18446744073707462447

@nielsdos
Copy link
Member

I can't test it myself, but I think this might fix it (apply on branch 8.1):

diff --git a/ext/standard/string.c b/ext/standard/string.c
index 8a223b72f4..254f2d0458 100644
--- a/ext/standard/string.c
+++ b/ext/standard/string.c
@@ -3989,18 +3989,22 @@ static zend_always_inline char *php_stripslashes_impl(const char *str, char *out
                vst1q_u8(q.mem, vceqq_u8(x, vdupq_n_u8('\\')));
                if (q.dw[0] | q.dw[1]) {
                        int i = 0;
-                       for (; i < 16; i++) {
+                       while (i < 16) {
                                if (q.mem[i] == 0) {
                                        *out++ = str[i];
+                                       i++;
                                        continue;
                                }
 
                                i++;                    /* skip the slash */
-                               char s = str[i];
-                               if (s == '0')
-                                       *out++ = '\0';
-                               else
-                                       *out++ = s;     /* preserve the next character */
+                               if (i < len) {
+                                       char s = str[i];
+                                       if (s == '0')
+                                               *out++ = '\0';
+                                       else
+                                               *out++ = s;     /* preserve the next character */
+                                       i++;
+                               }
                        }
                        str += i;
                        len -= i;

@todeveni
Copy link
Contributor Author

Well it doesn't segfault anymore and is giving out correct results 🙂
Patched and tested against the same php-src, which segfaulted.

~/php-src$ sapi/cli/php -r 'var_dump(stripslashes("1234567890abcde\\"));'
string(15) "1234567890abcde"
diff --git a/ext/standard/string.c b/ext/standard/string.c
index daed5b59cb..d246aac859 100644
--- a/ext/standard/string.c
+++ b/ext/standard/string.c
@@ -3822,18 +3822,22 @@ static zend_always_inline char *php_stripslashes_impl(const char *str, char *out
 		vst1q_u8(q.mem, vceqq_u8(x, vdupq_n_u8('\\')));
 		if (q.dw[0] | q.dw[1]) {
 			int i = 0;
-			for (; i < 16; i++) {
+			while (i < 16) {
 				if (q.mem[i] == 0) {
 					*out++ = str[i];
+					i++;
 					continue;
 				}
 
 				i++;			/* skip the slash */
-				char s = str[i];
-				if (s == '0')
-					*out++ = '\0';
-				else
-					*out++ = s;	/* preserve the next character */
+				if (i < len) {
+					char s = str[i];
+					if (s == '0')
+						*out++ = '\0';
+					else
+						*out++ = s;     /* preserve the next character */
+					i++;
+                               }
 			}
 			str += i;
 			len -= i;

@devnexen
Copy link
Member

if (i < len) {
+                                       char s = str[i];
+                                       if (s == '0')
+                                               *out++ = '\0';
+                                       else
+                                               *out++ = s;     /* preserve the next character */
+                                       i++;
+                               }

can you make a PR please ?

@nielsdos
Copy link
Member

I'll make a PR and add a .phpt

nielsdos added a commit to nielsdos/php-src that referenced this issue Dec 30, 2022
nielsdos added a commit to nielsdos/php-src that referenced this issue Dec 30, 2022
Co-authored-by: todeveni <toni.viemero@iki.fi>
nielsdos added a commit to nielsdos/php-src that referenced this issue Dec 30, 2022
nielsdos added a commit to nielsdos/php-src that referenced this issue Dec 30, 2022
Co-authored-by: todeveni <toni.viemero@iki.fi>
@cmb69
Copy link
Contributor

cmb69 commented Dec 30, 2022

if (s == '0')

looks fishy. Shouldn't that be if (s == '\0') instead? In which case the whole if-else could be simplified.

Disregard; that code is correct.

Girgias added a commit that referenced this issue Dec 30, 2022
* PHP-8.1:
  Fix GH-10187: Segfault in stripslashes() with arm64
  Fix memory leak in posix_ttyname()
Girgias added a commit that referenced this issue Dec 30, 2022
* PHP-8.2:
  Fix GH-10187: Segfault in stripslashes() with arm64
  Fix memory leak in posix_ttyname()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants
@todeveni @cmb69 @devnexen @nielsdos and others