可變參數中size_t遇見的問題
在修改php擴展Trie時,出現了一個小bug
PHP_FUNCTION(trie_filter_load) { Trie *trie; char *path; int path_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &path, &path_len) == FAILURE) { RETURN_NULL(); } if(path !=NULL){ php_printf("path is not null\n"); php_printf("path address is %x\n", path); } trie = trie_new_from_file(path); if (!trie) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to load %s", path); RETURN_NULL(); } RETURN_RES(zend_register_resource(trie, le_trie_filter)); }
註意這個 path_len的類型為int
運行結果為
start print the path
print the path
the path is not null
the path address is 0x7fdd00000000
Segmentation fault
#設置core大小為無限,從而產生core文件
ulimit -c unlimited
gdb /usr/local/php-7.1.6/bin/php core.22772
core was generated by `/usr/local/php-7.1.6/bin/php test.php‘. Program terminated with signal 11, Segmentation fault. #0 0x00007fb3433bb301 in __strlen_sse2 () from /lib64/libc.so.6 Missing separate debuginfos, use: debuginfo-install cyrus-sasl-lib-2.1.23-15.el6_6.2.x86_64 glibc-2.12-1.192.el6.x86_64 keyutils-libs-1.4-5.el6.x86_64 krb5-libs-1.10.3-57.el6.x86_64 libcom_err-1.41.12-22.el6.x86_64 libcurl-7.19.7-53.el6_9.x86_64 libidn-1.18-2.el6.x86_64 libselinux-2.0.94-7.el6.x86_64 libssh2-1.4.2-2.el6_7.1.x86_64 libxml2-2.7.6-21.el6_8.1.x86_64 nspr-4.13.1-1.el6.x86_64 nss-3.28.4-3.el6_9.x86_64 nss-softokn-freebl-3.14.3-23.el6_7.x86_64 nss-util-3.28.4-1.el6_9.x86_64 openldap-2.4.40-12.el6.x86_64 openssl-1.0.1e-57.el6.x86_64 (gdb) where #0 0x00007fb3433bb301 in __strlen_sse2 () from /lib64/libc.so.6 #1 0x000000000093d5c5 in xbuf_format_converter (xbuf=0x7ffc60473cf0, is_char=1 ‘\001‘, fmt=0x7fb33a76c0c8 "trie_filter_search_all", ap=0x7ffc60473e60) at /home/source/php-7.1.6/main/spprintf.c:605 #2 0x000000000093e762 in vspprintf (pbuf=0x7ffc60473da8, max_len=0, format=0x7fb33a76c0b8 "e_filter_search", ap=0x7ffc60473e60) at /home/source/php-7.1.6/main/spprintf.c:843 #3 0x00000000009345e1 in php_verror (docref=0x0, params=0x1031a72 "", type=2, format=0x7fb33a76c0b8 "e_filter_search", args=0x7ffc60473e60) at /home/source/php-7.1.6/main/main.c:762 #4 0x0000000000935075 in php_error_docref0 (docref=0x0, type=2, format=0x7fb33a76c0b8 "e_filter_search") at /home/source/php-7.1.6/main/main.c:949 #5 0x00007fb33a76b4ea in zif_trie_filter_load (execute_data=0x7fb340814260, return_value=0x7fb340814200) at /home/source/php-7.1.8/ext/php-ext-trie-filter/trie_filter.c:124 #6 0x0000000000a317f9 in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER (execute_data=0x7fb3408140d0) at /home/source/php-7.1.6/Zend/zend_vm_execute.h:675 #7 0x0000000000a30e53 in execute_ex (ex=0x7fb340814030) at /home/source/php-7.1.6/Zend/zend_vm_execute.h:432 #8 0x0000000000a30fa4 in zend_execute (op_array=0x7fb34086d100, return_value=0x0) at /home/source/php-7.1.6/Zend/zend_vm_execute.h:474 #9 0x00000000009d0543 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /home/source/php-7.1.6/Zend/zend.c:1476 #10 0x00000000009386de in php_execute_script (primary_file=0x7ffc60477580) at /home/source/php-7.1.6/main/main.c:2537 #11 0x0000000000ac4ab6 in do_cli (argc=2, argv=0x2b1cb70) at /home/source/php-7.1.6/sapi/cli/php_cli.c:993 #12 0x0000000000ac59f5 in main (argc=2, argv=0x2b1cb70) at /home/source/php-7.1.6/sapi/cli/php_cli.c:1381 (gdb)
發現是 調用 __strlen_sse2 時出現問題,利用where 找到調用 的 __strlen_sse2的棧幀, 估計bt也可以吧
就是說path指針 指向了 地址為0x7fdd00000000的內存,但在執行__strlen_sse2的時候 出問題了,說明這個內存地址 有問題
而 /home/source/php-7.1.6/main/spprintf.c:605 的代碼是
case ‘s‘: case ‘v‘: s = va_arg(ap, char *); if (s != NULL) { if (!adjust_precision) { s_len = strlen(s); //506行 } else { s_len = strnlen(s, precision); } } else { s = S_NULL; s_len = S_NULL_LEN; } pad_char = ‘ ‘; break;
解決方法
1)目前調用的函數是trie_filter_load, 但在擴展中是zif_trie_filter_load
利用nm找到在擴展中找到
[[email protected] ~]# nm /usr/local/php-7.1.6/lib/php/extensions/debug-non-zts-20160303/trie_filter.so|grep trie_filter_load 00000000000014bd T zif_trie_filter_load
2)利用gdb調試
gdb /usr/local/php-7.1.6/bin/php
(gdb) br zif_trie_filter_load
(gdb) r ./test.php
Breakpoint 1, zif_trie_filter_load (execute_data=0x7ffff2e14260, return_value=0x7ffff2e14200)
at /home/source/php-7.1.8/ext/php-ext-trie-filter/trie_filter.c:125
warning: Source file is more recent than executable.
125 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
(gdb) n
(gdb) p path
$1 = 0x7fff00000000 <Address 0x7fff00000000 out of bounds>
地址居然越界了
可以肯定的是在執行 php_error_docref時報錯了
稍帶看下zend_parse_parameter的原理,其參數 是 可變參數
ZEND_API int zend_parse_parameter(int flags, int arg_num, zval *arg, const char *spec, ...) { va_list va; int ret; va_start(va, spec); ret = zend_parse_arg(arg_num, arg, &va, &spec, flags); va_end(va); return ret; }
static int zend_parse_arg(int arg_num, zval *arg, va_list *va, const char **spec, int flags) /* {{{ */ { const char *expected_type = NULL; char *error = NULL; int severity = 0; expected_type = zend_parse_arg_impl(arg_num, arg, va, spec, &error, &severity); if (expected_type) { if (!(flags & ZEND_PARSE_PARAMS_QUIET) && (*expected_type || error)) { const char *space; const char *class_name = get_active_class_name(&space); zend_bool throw_exception = ZEND_ARG_USES_STRICT_TYPES() || (flags & ZEND_PARSE_PARAMS_THROW); if (error) { zend_internal_type_error(throw_exception, "%s%s%s() expects parameter %d %s", class_name, space, get_active_function_name(), arg_num, error); efree(error); } else { zend_internal_type_error(throw_exception, "%s%s%s() expects parameter %d to be %s, %s given", class_name, space, get_active_function_name(), arg_num, expected_type, zend_zval_type_name(arg)); } } if (severity != E_DEPRECATED) { return FAILURE; } } return SUCCESS; }
因為傳入的是string,所以進入case ‘s‘,其接收的類型為size_t,即
因為執行該程序所在的機器是x86 64位,size_t的宏定義為 typedef unsigned long size_t;
如果機器是x86 32位,size_t宏的定義為 typedef unsigned int size_t;
定義是int類型,接收為unsigned long 肯定不行啊
關於可變參數的原理,詳見這裏
static const char *zend_parse_arg_impl(int arg_num, zval *arg, va_list *va, const char **spec, char **error, int *severity) /* {{{ */ { const char *spec_walk = *spec; char c = *spec_walk++; int check_null = 0; int separate = 0; zval *real_arg = arg; /* scan through modifiers */ ZVAL_DEREF(arg); while (1) { if (*spec_walk == ‘/‘) { SEPARATE_ZVAL_NOREF(arg); real_arg = arg; separate = 1; } else if (*spec_walk == ‘!‘) { check_null = 1; } else { break; } spec_walk++; } switch (c) { case ‘l‘: case ‘L‘: { zend_long *p = va_arg(*va, zend_long *); zend_bool *is_null = NULL; if (check_null) { is_null = va_arg(*va, zend_bool *); } if (!zend_parse_arg_long(arg, p, is_null, check_null, c == ‘L‘)) { return "integer"; } } break; case ‘d‘: { double *p = va_arg(*va, double *); zend_bool *is_null = NULL; if (check_null) { is_null = va_arg(*va, zend_bool *); } if (!zend_parse_arg_double(arg, p, is_null, check_null)) { return "float"; } } break; case ‘s‘: { char **p = va_arg(*va, char **); size_t *pl = va_arg(*va, size_t *); if (!zend_parse_arg_string(arg, p, pl, check_null)) { return "string"; } } break; case ‘p‘: { char **p = va_arg(*va, char **); size_t *pl = va_arg(*va, size_t *); if (!zend_parse_arg_path(arg, p, pl, check_null)) { return "a valid path"; } } break; case ‘P‘: { zend_string **str = va_arg(*va, zend_string **); if (!zend_parse_arg_path_str(arg, str, check_null)) { return "a valid path"; } } break; case ‘S‘: { zend_string **str = va_arg(*va, zend_string **); if (!zend_parse_arg_str(arg, str, check_null)) { return "string"; } } break; case ‘b‘: { zend_bool *p = va_arg(*va, zend_bool *); zend_bool *is_null = NULL; if (check_null) { is_null = va_arg(*va, zend_bool *); } if (!zend_parse_arg_bool(arg, p, is_null, check_null)) { return "boolean"; } } break; case ‘r‘: { zval **p = va_arg(*va, zval **); if (!zend_parse_arg_resource(arg, p, check_null)) { return "resource"; } } break; case ‘A‘: case ‘a‘: { zval **p = va_arg(*va, zval **); if (!zend_parse_arg_array(arg, p, check_null, c == ‘A‘)) { return "array"; } } break; case ‘H‘: case ‘h‘: { HashTable **p = va_arg(*va, HashTable **); if (!zend_parse_arg_array_ht(arg, p, check_null, c == ‘H‘, separate)) { return "array"; } } break; case ‘o‘: { zval **p = va_arg(*va, zval **); if (!zend_parse_arg_object(arg, p, NULL, check_null)) { return "object"; } } break; case ‘O‘: { zval **p = va_arg(*va, zval **); zend_class_entry *ce = va_arg(*va, zend_class_entry *); if (!zend_parse_arg_object(arg, p, ce, check_null)) { if (ce) { return ZSTR_VAL(ce->name); } else { return "object"; } } } break; case ‘C‘: { zend_class_entry *lookup, **pce = va_arg(*va, zend_class_entry **); zend_class_entry *ce_base = *pce; if (check_null && Z_TYPE_P(arg) == IS_NULL) { *pce = NULL; break; } convert_to_string_ex(arg); if ((lookup = zend_lookup_class(Z_STR_P(arg))) == NULL) { *pce = NULL; } else { *pce = lookup; } if (ce_base) { if ((!*pce || !instanceof_function(*pce, ce_base))) { zend_spprintf(error, 0, "to be a class name derived from %s, ‘%s‘ given", ZSTR_VAL(ce_base->name), Z_STRVAL_P(arg)); *pce = NULL; return ""; } } if (!*pce) { zend_spprintf(error, 0, "to be a valid class name, ‘%s‘ given", Z_STRVAL_P(arg)); return ""; } break; } break; case ‘f‘: { zend_fcall_info *fci = va_arg(*va, zend_fcall_info *); zend_fcall_info_cache *fcc = va_arg(*va, zend_fcall_info_cache *); char *is_callable_error = NULL; if (check_null && Z_TYPE_P(arg) == IS_NULL) { fci->size = 0; fcc->initialized = 0; break; } if (zend_fcall_info_init(arg, 0, fci, fcc, NULL, &is_callable_error) == SUCCESS) { if (is_callable_error) { *severity = E_DEPRECATED; zend_spprintf(error, 0, "to be a valid callback, %s", is_callable_error); efree(is_callable_error); *spec = spec_walk; return ""; } break; } else { if (is_callable_error) { *severity = E_ERROR; zend_spprintf(error, 0, "to be a valid callback, %s", is_callable_error); efree(is_callable_error); return ""; } else { return "valid callback"; } } } case ‘z‘: { zval **p = va_arg(*va, zval **); zend_parse_arg_zval_deref(real_arg, p, check_null); } break; case ‘Z‘: /* ‘Z‘ iz not supported anymore and should be replaced with ‘z‘ */ ZEND_ASSERT(c != ‘Z‘); default: return "unknown"; } *spec = spec_walk; return NULL; }
參考 size_t 這裏
http://www.360doc.com/content/12/0804/11/3725126_228273988.shtml
http://www.cnblogs.com/cpoint/p/3368993.html
http://blog.csdn.net/striver1205/article/details/25799523
http://blog.csdn.net/hudashi/article/details/7820338
http://blog.csdn.net/chenlycly/article/details/37912755
http://blog.csdn.net/zhouzhaoxiong1227/article/details/48976481
http://www.yiibai.com/c_standard_library/c_macro_va_arg.html
http://blog.sina.com.cn/s/blog_7eddff0b01010umd.html
可變參數中size_t遇見的問題