可变参数中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

时间: 2024-07-28 22:41:52

可变参数中size_t遇见的问题的相关文章

接口,泛型,可变参数在代码向上抽去中的应用探究

JAVA作为一种面向对象的语言,类和对象可以说是搭建起JAVA体系的基本框架,我们可以将类看做是对众多实体对象共性的向上抽取,而接口则是对类共性的向上抽取,我将接口理解为一种规则,一种规范,同时也是多态的应用中我们作为形式参数传递的最顶层的父类,因此接口的功能非常强大(能装B),我们在sun公司定义的API中可以经常看到它的身影,它也是架构师手中搭建框架的利器,因此以后我们会经常使用到它(能装B). ------------------------------------------------

c 语言函数可变参数的处理

/************************************************************************* > File Name: va_list.c > Author: zshh0604 > Mail: [email protected] > Created Time: 2014年10月14日 星期二 15时16分09秒 **********************************************************

Java之可变参数

Java中支持可变参数 意思就是:参数的个数可以根据需要写,你可以写1个.2个.3个....他们都被保存到一个参数的数组中. 但是这些参有一些约束:他们必须是同类型的,比如都是String字符串类型. 同时,可变参数的函数中的参数的写法也有约束:比如,可变参数的数组必须写在参数的最后,否则程序不知道你的参数到底有多少个. 例子:输出可变参数中的参数值 public class VariableArgument {    public static void main(String[] args)

函数的可变参数

一.可变参数介绍: 1.一个方法定义好了参数,在调用时必须传入与其一一对应的参数 2.但是在JDK1.5之后提供了新的功能,可以根据需要自动传入任意个数的参数 3.其实就是一个数组,但是接收的是数组的元素,自动将这些元素封装成数组,简化了调用者的书写(注意:可变参数类型必须定义在参数列表的结尾处) 4.可变参数语法: 返回值类型 方法名称(数据类型....参数名称){ } 二.代码实例 public class ParamterDemo { public static void main(Str

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

 1.volatile:要求参数修改每次都从内存中的读取.这种情况要比普通运行的变量需要的时间长. #include <stdio.h> #include <stdlib.h> #include <time.h> void main() { time_t start, end; double res = 0; time(&start);  //获取时间,传递给start //volatile强制每次从内存读取 volatile int i; for (i =

_vsnprintf在可变参数打印中的用法

_vsnprintf,C语言库函数之一,属于可变参数.用于向字符串中打印数据.数据格式用户自定义. 函数简介 编辑 头文件: #include <stdarg.h> 函数声明: int _vsnprintf(char* str, size_t size, const char* format, va_list ap); 参数说明: char *str [out],把生成的格式化的字符串存放在这里. size_t size [in], str可接受的最大字符数[1]  (非字节数,UNICODE

C++中的可变参数模板

作者:Eli Bendersky http://eli.thegreenplace.net/2014/variadic-templates-in-c/ 回到C++11前夜,编写带有任意参数函数的唯一办法是使用可变参数函数,像printf,使用省略号语法(-)以及伴随的va_族的宏.如果你曾经使用这个方法编写代码,你会知道这有多累赘.除了变成类型不安全外(所有的类型解析必须在运行时在va_arg里通过显式转换来完成),要做对并不容易.Va_宏执行低级的内存操作,我看见过许多因为没有小心使用它们导致

求变量的数据类型,typeid,bool,C和C++的不同,new和delete,C++中的枚举,inline和可变参数模板,auto和函数模板,宽字符

求变量的数据类型,通过函数typeid(变量名).name();获得变量的数据类型. 案例如下: #include <iostream> #include <stdlib.h> void main() { double db = 10.9; double *pdb = &db; auto num = pdb; //通过typeid的方式获得数据类型 std::cout << typeid(db).name() << std::endl; std::c

【C语言】求多个数中的最大值(可变参数列表)

求多个数中的最大值要求用可变参数列表: 代码如下: <span style="font-size:18px;">#include<stdio.h> #include<stdarg.h> int Max(int n,...) { int i=0; int max=0; va_list arg; va_start(arg,n); for(i=0;i<n;i++) { int val=va_arg(arg,int); if (val>max)