在修改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./bin/php test.php'.
Program terminated with signal , Segmentation fault.
# 0x00007fb3433bb301 in __strlen_sse2 () from /lib64/libc.so.
Missing separate debuginfos, use: debuginfo-install cyrus-sasl-lib-2.1.-.el6_6..x86_64 glibc-2.12-1.192.el6.x86_64 keyutils-libs-1.4-.el6.x86_64 krb5-libs-1.10.-.el6.x86_64 libcom_err-1.41.-.el6.x86_64 libcurl-7.19.-.el6_9.x86_64 libidn-1.18-.el6.x86_64 libselinux-2.0.-.el6.x86_64 libssh2-1.4.-.el6_7..x86_64 libxml2-2.7.-.el6_8..x86_64 nspr-4.13.-.el6.x86_64 nss-3.28.-.el6_9.x86_64 nss-softokn-freebl-3.14.-.el6_7.x86_64 nss-util-3.28.-.el6_9.x86_64 openldap-2.4.-.el6.x86_64 openssl-1.0.1e-.el6.x86_64
(gdb) where
# 0x00007fb3433bb301 in __strlen_sse2 () from /lib64/libc.so.
# 0x000000000093d5c5 in xbuf_format_converter (xbuf=0x7ffc60473cf0, is_char= '\001', fmt=0x7fb33a76c0c8 "trie_filter_search_all", ap=0x7ffc60473e60)
at /home/source/php-7.1./main/spprintf.c:
# 0x000000000093e762 in vspprintf (pbuf=0x7ffc60473da8, max_len=, format=0x7fb33a76c0b8 "e_filter_search", ap=0x7ffc60473e60) at /home/source/php-7.1./main/spprintf.c:
# 0x00000000009345e1 in php_verror (docref=0x0, params=0x1031a72 "", type=, format=0x7fb33a76c0b8 "e_filter_search", args=0x7ffc60473e60) at /home/source/php-7.1./main/main.c:
# 0x0000000000935075 in php_error_docref0 (docref=0x0, type=, format=0x7fb33a76c0b8 "e_filter_search") at /home/source/php-7.1./main/main.c:
# 0x00007fb33a76b4ea in zif_trie_filter_load (execute_data=0x7fb340814260, return_value=0x7fb340814200) at /home/source/php-7.1./ext/php-ext-trie-filter/trie_filter.c:
# 0x0000000000a317f9 in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER (execute_data=0x7fb3408140d0) at /home/source/php-7.1./Zend/zend_vm_execute.h:
# 0x0000000000a30e53 in execute_ex (ex=0x7fb340814030) at /home/source/php-7.1./Zend/zend_vm_execute.h:
# 0x0000000000a30fa4 in zend_execute (op_array=0x7fb34086d100, return_value=0x0) at /home/source/php-7.1./Zend/zend_vm_execute.h:
# 0x00000000009d0543 in zend_execute_scripts (type=, retval=0x0, file_count=) at /home/source/php-7.1./Zend/zend.c:
# 0x00000000009386de in php_execute_script (primary_file=0x7ffc60477580) at /home/source/php-7.1./main/main.c:
# 0x0000000000ac4ab6 in do_cli (argc=, argv=0x2b1cb70) at /home/source/php-7.1./sapi/cli/php_cli.c:
# 0x0000000000ac59f5 in main (argc=, argv=0x2b1cb70) at /home/source/php-7.1./sapi/cli/php_cli.c:
(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找到在扩展中找到

[root@www ~]# nm /usr/local/php-7.1./lib/php/extensions/debug-non-zts-/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_parameters(int num_args, const char *type_spec, ...) /* {{{ */
{
va_list va;
int retval;
int flags = ; va_start(va, type_spec);
retval = zend_parse_va_args(num_args, type_spec, &va, flags);
va_end(va); return retval;
}
static int zend_parse_va_args(int num_args, const char *type_spec, va_list *va, int flags) /* {{{ */
{
const char *spec_walk;
int c, i;
int min_num_args = -;
int max_num_args = ;
int post_varargs = ;
zval *arg;
int arg_count;
zend_bool have_varargs = ;
zval **varargs = NULL;
int *n_varargs = NULL; for (spec_walk = type_spec; *spec_walk; spec_walk++) {
c = *spec_walk;
switch (c) {
case 'l': case 'd':
case 's': case 'b':
case 'r': case 'a':
case 'o': case 'O':
case 'z': case 'Z':
case 'C': case 'h':
case 'f': case 'A':
case 'H': case 'p':
case 'S': case 'P':
case 'L':
max_num_args++;
break; case '|':
min_num_args = max_num_args;
break; case '/':
case '!':
/* Pass */
break; case '*':
case '+':
if (have_varargs) {
zend_parse_parameters_debug_error(
"only one varargs specifier (* or +) is permitted");
return FAILURE;
}
have_varargs = ;
/* we expect at least one parameter in varargs */
if (c == '+') {
max_num_args++;
}
/* mark the beginning of varargs */
post_varargs = max_num_args;
break; default:
zend_parse_parameters_debug_error("bad type specifier while parsing parameters");
return FAILURE;
}
} if (min_num_args < ) {
min_num_args = max_num_args;
} if (have_varargs) {
/* calculate how many required args are at the end of the specifier list */
post_varargs = max_num_args - post_varargs;
max_num_args = -;
} if (num_args < min_num_args || (num_args > max_num_args && max_num_args >= )) {
if (!(flags & ZEND_PARSE_PARAMS_QUIET)) {
zend_function *active_function = EG(current_execute_data)->func;
const char *class_name = active_function->common.scope ? ZSTR_VAL(active_function->common.scope->name) : "";
zend_bool throw_exception = ZEND_ARG_USES_STRICT_TYPES() || (flags & ZEND_PARSE_PARAMS_THROW);
zend_internal_argument_count_error(throw_exception, "%s%s%s() expects %s %d parameter%s, %d given",
class_name,
class_name[] ? "::" : "",
ZSTR_VAL(active_function->common.function_name),
min_num_args == max_num_args ? "exactly" : num_args < min_num_args ? "at least" : "at most",
num_args < min_num_args ? min_num_args : max_num_args,
(num_args < min_num_args ? min_num_args : max_num_args) == ? "" : "s",
num_args);
}
return FAILURE;
} arg_count = ZEND_CALL_NUM_ARGS(EG(current_execute_data)); if (num_args > arg_count) {
zend_parse_parameters_debug_error("could not obtain parameters for parsing");
return FAILURE;
} i = ;
while (num_args-- > ) {
if (*type_spec == '|') {
type_spec++;
} if (*type_spec == '*' || *type_spec == '+') {
int num_varargs = num_args + - post_varargs; /* eat up the passed in storage even if it won't be filled in with varargs */
varargs = va_arg(*va, zval **);
n_varargs = va_arg(*va, int *);
type_spec++; if (num_varargs > ) {
*n_varargs = num_varargs;
*varargs = ZEND_CALL_ARG(EG(current_execute_data), i + );
/* adjust how many args we have left and restart loop */
num_args += - num_varargs;
i += num_varargs;
continue;
} else {
*varargs = NULL;
*n_varargs = ;
}
} arg = ZEND_CALL_ARG(EG(current_execute_data), i + 1); if (zend_parse_arg(i+, arg, va, &type_spec, flags) == FAILURE) {
/* clean up varargs array if it was used */
if (varargs && *varargs) {
*varargs = NULL;
}
return FAILURE;
}
i++;
} return SUCCESS;
}
/* }}} */
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 = ; 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 = ;
int separate = ;
zval *real_arg = arg; /* scan through modifiers */
ZVAL_DEREF(arg);
while () {
if (*spec_walk == '/') {
SEPARATE_ZVAL_NOREF(arg);
real_arg = arg;
separate = ;
} else if (*spec_walk == '!') {
check_null = ;
} 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, , "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, , "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 = ;
fcc->initialized = ;
break;
} if (zend_fcall_info_init(arg, , fci, fcc, NULL, &is_callable_error) == SUCCESS) {
if (is_callable_error) {
*severity = E_DEPRECATED;
zend_spprintf(error, , "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, , "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;
}
static zend_always_inline int zend_parse_arg_string(zval *arg, char **dest, size_t *dest_len, int check_null)
{
zend_string *str; if (!zend_parse_arg_str(arg, &str, check_null)) {
return ;
}
if (check_null && UNEXPECTED(!str)) {
*dest = NULL;
*dest_len = ;
} else {
*dest = ZSTR_VAL(str);
*dest_len = ZSTR_LEN(str);
}
return ;
}
static zend_always_inline int zend_parse_arg_str(zval *arg, zend_string **dest, int check_null)
{
if (EXPECTED(Z_TYPE_P(arg) == IS_STRING)) {
*dest = Z_STR_P(arg);
} else if (check_null && Z_TYPE_P(arg) == IS_NULL) {
*dest = NULL;
} else {
return zend_parse_arg_str_slow(arg, dest);
}
return ;
}

参考 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遇见的问题的更多相关文章

  1. C语言可变参数宏及‘##’在可变参数中的作用

    测试代码及解释: #include <stdio.h> #define PRINT(x) printf x #define SECONDPRINT(fmt,arg...) printf(f ...

  2. [lua] 你所不知道的lua nil值在可变参数函数中怎么处理!

    在lua中, 问题1:如果你在可变参数...中传入若干个参数,其中有的参数要带nil,这时怎么解决呢?(比如local function _test(...) end    _test(1, nil, ...

  3. 关于Retrofit网络请求URL中含有可变参数的处理

    开题:在此默认各位看官对Retrofit.以及Okhttp已经有过一定的了解及应用,所以今天我们不谈基础入门的东西,今天我们谈在Retrofit请求接口管理类中URL参数含有动态参数的处理方式.一般我 ...

  4. Java之可变参数

    Java中支持可变参数 意思就是:参数的个数可以根据需要写,你可以写1个.2个.3个....他们都被保存到一个参数的数组中. 但是这些参有一些约束:他们必须是同类型的,比如都是String字符串类型. ...

  5. Effective Java 第三版——53. 明智而审慎地使用可变参数

    Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...

  6. C++.可变参数_ZC测试

    ZC:环境: Win7 x64(旗舰版),Microsoft Visual Studio 2010(版本 10.0.30319.1 RTMRel, Microsoft .NET Framework(版 ...

  7. 可变参数__VA_ARGS__使用

    1. 调试功能一般会使用到宏+可变参数的方式 1.1 ##__VA_ARGS__      之详细解析 例如: case A. #define my_print1(...)    printf(__V ...

  8. volatile,可变参数,memset,内联函数,宽字符窄字符,国际化,条件编译,预处理命令,define中##和#的区别,文件缓冲,位域

    1.volatile: 要求参数修改每次都从内存中的读取.这种情况要比普通运行的变量需要的时间长. 当设置了成按照C99标准运行之后,使用volatile变量之后的程序运行的时间将比register的 ...

  9. Java中不定项参数(可变参数)的作用和使用方式

    引言: 我们在编写方法的过程中,可能会遇见一个方法有不确定参数个数的情况.一般我们会用方法重载来解决问题: //方法重载,解决参数个数不确定问题 public void method(); publi ...

随机推荐

  1. Ajax工作原理和原生JS的ajax封装

    前言: 之所以用ajax作为博客的开篇,是因为无论从ajax的出现还是从它的作用上来说,ajax对于前端无疑是意义重大的.甚至可以说,是ajax带来了前端这个行业.当然,历史并不能说明当下,曾经的辉煌 ...

  2. socket,TCP/IP的理解(转)

    TCP/IP 要想理解socket首先得熟悉一下TCP/IP协议族, TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间 ...

  3. 647. Palindromic Substrings 互文的子字符串

    [抄题]: Given a string, your task is to count how many palindromic substrings in this string. The subs ...

  4. 并查集 - 1611 The Suspects

    题目地址: http://poj.org/problem?id=1611 分析: - 数据结构 - parent[x] 表示 x 元素的父节点位置. - rank[x] 记录x的子链的长度, 以便在合 ...

  5. 样条曲线catmull rom转bezier

    b0,..,b3是贝塞尔,c-1, c2是catmull rom控制点 [b0] = 1 [ 0 6 0 0] [c_1] [b1] - [-1 6 1 0] [c0] [b2] 6 [ 0 1 6 ...

  6. 云存储上传控件更新日志-Xproer.cloud2

    官方网站:http://www.ncmem.com/ 产品首页:http://www.ncmem.com/webapp/cloud2/index.asp 在线演示:http://www.ncmem.c ...

  7. unity 确定敌人行走路线

    一开始搞这个问题很头疼,无从下手. 1.敌人在随机地点产生后,每个敌人有要有自己自动的行走路线,目的地是保护地,而且行走路线要多样化. 2.敌人在看到玩家时,改变行走路线,向玩家的方向行进,且到了一定 ...

  8. 练习题。对DOM中document的深刻理解巩固

    //window.onload = modTwo;     1.点击单元格内容  弹窗promrt接收值   将接受的值提换单元格内容    2.点击单元格  出现2个按钮 加粗 字体颜色标红     ...

  9. 关于validate的自定义样式

    $("#Form").validate({ rules: { name: "required" }, messages: { name: "请输入您的 ...

  10. POJ 3660 Cow Contest(传递闭包)

    N (1 ≤ N ≤ 100) cows, conveniently numbered 1..N, are participating in a programming contest. As we ...