• 可变参数中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
    ");
            php_printf("path address is %x
    ", 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 '01', 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找到在扩展中找到

    [root@www ~]# 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_parameters(int num_args, const char *type_spec, ...) /* {{{ */
    {
        va_list va;
        int retval; 
        int flags = 0;
    
        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 = -1;
        int max_num_args = 0;
        int post_varargs = 0;
        zval *arg;
        int arg_count;
        zend_bool have_varargs = 0;
        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 = 1;
                    /* 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 < 0) {
            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 = -1;
        }
    
        if (num_args < min_num_args || (num_args > max_num_args && max_num_args >= 0)) {
            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[0] ? "::" : "",
                        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) == 1 ? "" : "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 = 0;
        while (num_args-- > 0) {
            if (*type_spec == '|') {
                type_spec++;
            }
    
            if (*type_spec == '*' || *type_spec == '+') {
                int num_varargs = num_args + 1 - 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 > 0) {
                    *n_varargs = num_varargs;
                    *varargs = ZEND_CALL_ARG(EG(current_execute_data), i + 1);
                    /* adjust how many args we have left and restart loop */
                    num_args += 1 - num_varargs;
                    i += num_varargs;
                    continue;
                } else {
                    *varargs = NULL;
                    *n_varargs = 0;
                }
            }
    
            arg = ZEND_CALL_ARG(EG(current_execute_data), i + 1);
    
            if (zend_parse_arg(i+1, 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 = 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;
    }
    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 0;
        }
        if (check_null && UNEXPECTED(!str)) {
            *dest = NULL;
            *dest_len = 0;
        } else {    
            *dest = ZSTR_VAL(str);
            *dest_len = ZSTR_LEN(str);
        }
        return 1;
    }
    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 1;
    }

    参考 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

  • 相关阅读:
    POJ2828
    Docker容器修改端口映射
    CentOS 7使用ISO镜像配置本地yum源
    Windows 自带的 Linux 子系统
    vue富文本编辑器插件vue-quill-editor使用
    修改docker容器存放位置
    Linux 硬盘相关操作
    centos7基础相关
    ubuntu-k8s搭建
    redhat 6.6 离线docker
  • 原文地址:https://www.cnblogs.com/taek/p/7460592.html
Copyright © 2020-2023  润新知