【问题标题】:Invalid Pointer: free()无效指针:free()
【发布时间】:2012-05-05 23:28:11
【问题描述】:

在第 4 行的 process_query_str 中,我调用 splitline 并返回一个包含 3 个元素的字符串数组,其中最后一个是 NULL。数组被分配,各个字符串也是如此。然后我尝试释放每个元素,然后释放数组本身(接近第一个函数的末尾)。前两个字符串被释放,但是当它释放第三个元素时,即 NULL,我在底部得到关于无效指针的错误。显然它没有被设置为 NULL 并且应该是。我尝试放置断点,但程序不会停止。

char * process_query_str(char *rq, char *cmd) {
    logg("In process_query_str.\n");
    int has_query = 0;
    char **tokenized_args = splitline(rq, &has_query); //Tokenize the string based on '?'
    char * query_str = rq;
    logg("has_query:");
    fprintf(stderr, "%d", has_query);
    if (has_query == 1) //Check for Query String
        if (query_str != NULL) {
            query_str = retrieve_query_str(tokenized_args);
            logg("Query String: ");
            logg(query_str);
            logg("\n");

            char* key = "REQUEST_METHOD"; //Process REQUEST_METHOD envir variable
            if (VLstore(key, cmd) == 0)
                if (VLexport(key) == 0) {
                    logg("Successfully exported ");
                    logg(cmd);
                    logg(": ");
                    logg(key);
                    logg("\n");

                    key = "QUERY_STRING"; //Process QUERY_STRING envir variable
                    if (VLstore(key, query_str) != 1) //1 signals a problem
                        if (VLexport(key) != 0) //0 signals a problem
                                {
                            logg("Successfully exported ");
                            logg(cmd);
                            logg(": ");
                            logg(key);
                            logg("\n");
                        }
                }
        }
#ifdef LOGGING //Print out environment variables
    VLlist();
#endif
    char *resource_str = newstr(tokenized_args[0], strlen(tokenized_args[0]));
    freelist(tokenized_args);
    logg("resource_str=");
    logg(resource_str);
    logg("\n");
    return resource_str;
}

char ** splitline(char *line, int*has_query)
/*
 * purpose: split a line into array of white-space separated tokens
 * returns: a NULL-terminated array of pointers to copies of the tokens
 *          or NULL if line if no tokens on the line
 *  action: traverse the array, locate strings, make copies
 *    note: strtok() could work, but we may want to add quotes later
 */
{
    //char *newstr();
    logg("In splitline\n");
    char **args;
    int spots = 0; /* spots in table    */
    int bufspace = 0; /* bytes in table */
    int argnum = 0; /* slots used       */
    char *cp = line; /* pos in string   */
    char *start;
    int len;

    if (line == NULL) /* handle special case    */
        return NULL;

    args = emalloc(BUFSIZ); /* initialize array */
    bufspace = BUFSIZ;
    spots = BUFSIZ / sizeof(char *);

    while (*cp != '\0') {
        logg("*cp=");
        fprintf(stderr, "%c", *cp);
        while (*cp == ' ') /* skip leading spaces   */
            cp++;
        if (*cp == '\0') /* quit at end-o-string    */
            break;
        /* make sure the array has room (+1 for NULL) */
        if (argnum + 1 >= spots) {
            args = erealloc(args, bufspace + BUFSIZ);
            bufspace += BUFSIZ;
            spots += (BUFSIZ / sizeof(char *));
        }

        /* mark start, then find end of word */
        start = cp;
        len = 1;
        if (*cp == '?') {
            logg("query reached.\n");
            *has_query = 1;
        }

        while (*++cp != '\0' && !(is_delim(*cp,*has_query)))
            len++;

        args[argnum++] = newstr(start, len);
    }
    logg("arg[0] =");
    logg(args[0]);
    logg("\n");
    if (argnum == 2) {
        logg("arg[1] =");
        logg(args[1]);
        logg("\n");
    }
    args[argnum] = NULL;
    fprintf(stderr, "last element is NULL.  argnum=%d", argnum);

    return args;
}

void freelist(char **list)
/*
 * purpose: free the list returned by splitline
 * returns: nothing
 *  action: free all strings in list and then free the list
 */
{
    char **cp = list;
    while (*cp && (*cp)) {
        logg("free: ");logg(*cp);logg("\n");
        free(*cp++);
    }
    logg("Now Free the list:");logg("\n");
    free(list);
    logg("Done Freeing List\n");
}

我从glibc得到的堆栈回溯信息是:

free: /index.cgifree: key=value*** glibc detected *** ./ws: free(): invalid pointer: 0x0804efa9 ***
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6[0xb76d3d05]
/lib/tls/i686/cmov/libc.so.6(cfree+0x90)[0xb76d7770]
./ws[0x804b36f]
./ws[0x804a1fb]
./ws[0x8049de1]
./ws[0x8049757]
./ws[0x8049660]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe0)[0xb767e460]
./ws[0x8049031]
======= Memory map: ========
08048000-0804d000 r-xp 00000000 00:24 8084895    /nfs/home/j/c/jcalderon/unixlin                                                                                                                     ux/wsng/ws
0804d000-0804e000 rw-p 00004000 00:24 8084895    /nfs/home/j/c/jcalderon/unixlin                                                                                                                     ux/wsng/ws
0804e000-0806f000 rw-p 0804e000 00:00 0          [heap]
b7500000-b7521000 rw-p b7500000 00:00 0
b7521000-b7600000 ---p b7521000 00:00 0
b7644000-b764e000 r-xp 00000000 68:07 1122448    /lib/libgcc_s.so.1
b764e000-b764f000 rw-p 0000a000 68:07 1122448    /lib/libgcc_s.so.1
b765c000-b7665000 r-xp 00000000 68:07 1122335    /lib/tls/i686/cmov/libnss_files                                                                                                                     -2.7.so
b7665000-b7667000 rw-p 00008000 68:07 1122335    /lib/tls/i686/cmov/libnss_files                                                                                                                     -2.7.so
b7667000-b7668000 rw-p b7667000 00:00 0
b7668000-b77b2000 r-xp 00000000 68:07 1122611    /lib/tls/i686/cmov/libc-2.7.so
b77b2000-b77b3000 r--p 0014a000 68:07 1122611    /lib/tls/i686/cmov/libc-2.7.so
b77b3000-b77b5000 rw-p 0014b000 68:07 1122611    /lib/tls/i686/cmov/libc-2.7.so
b77b5000-b77b8000 rw-p b77b5000 00:00 0
b77c2000-b77c7000 rw-p b77c2000 00:00 0
b77c7000-b77c8000 r-xp b77c7000 00:00 0          [vdso]
b77c8000-b77e2000 r-xp 00000000 68:07 1124008    /lib/ld-2.7.so
b77e2000-b77e4000 rw-p 00019000 68:07 1124008    /lib/ld-2.7.so
bfd9f000-bfdb4000 rw-p bffea000 00:00 0          [stack]

【问题讨论】:

  • 您需要通过删除更多不相关的内容来减少代码,然后再在此处发布。当你这样做时,你可能会自己找到问题的原因。
  • @user994165 - 为什么有这么多代码并期待答案 - 做好你的工作。还有为什么不告诉我们你的名字
  • @Mike 我已经减少了代码。
  • 这个想法应该是“保留可编译的代码”但“删除任何无关的东西”。
  • 我没有看到任何代码进行内存分配。

标签: c


【解决方案1】:

这不能解决您的问题,但可以帮助您减少代码量。您有如下片段:

logg("Successfully exported ");
logg(cmd);
logg(": ");
logg(key);
logg("\n");

如果你能写,这会更简单:

logg("Successfully exported %s: %s\n", cmd, key);

你可以通过修改这个函数来做到这一点:

#include <stdarg.h>
#include <stdio.h>

void logg(const char *format, ...)
{
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
}

你可以在标题中声明它:

#ifdef __GNUC__
#define PRINTFLIKE(n,m) __attribute__((format(printf,n,m)))
#else
#define PRINTFLIKE(n,m) /* If only */
#endif /* __GNUC__ */

extern void logg(const char *format, ...) PRINTFLIKE(1, 2);

然后 GCC 将发现格式转换规范与传递给函数的参数的滥用。

请注意,此版本的logg() 本质上与前一个版本兼容。唯一存在差异的情况是,正在打印的字符串是否包含类似 %Z 的字符串,该字符串现在被视为转换规范,而以前不是。不过,您应该使用 logg("%s\n", argument); 或类似名称。

【讨论】:

  • 我真的很喜欢那个 GCC 特定的格式说明符。好建议。
【解决方案2】:

原来是 malloc 问题,我没有在 newstr() 中分配足够的空间。当我试图释放它时,最后一个数组元素导致了错误。

【讨论】:

    【解决方案3】:

    我有一种感觉,这里有过度防御性的编程,并且有机会分解某些功能。

    考虑一下:

    char **tokenized_args = splitline(rq, &has_query);
    char * query_str = rq;
    logg("has_query:");
    fprintf(stderr, "%d", has_query);
    if (has_query == 1) //Check for Query String
        if (query_str != NULL) {
            query_str = retrieve_query_str(tokenized_args);
    

    query_str 被用于两件不同的事情——其中一件是非常短暂的。为什么要通过别名 query_str 检查 rq != NULL?为什么不直接测试rq != NULL?请注意,has_query == 1 只会是真正的 IFF rq != NULL

    has_query == 1query_str != NULL 之一是冗余,应完全删除。

    考虑一下:

            char* key = "REQUEST_METHOD";
            if (VLstore(key, cmd) == 0)
                if (VLexport(key) == 0) {
                    logg("Successfully exported ");
                    logg(cmd);
                    logg(": ");
                    logg(key);
                    logg("\n");
    

    这个日志记录可能应该包含在VLstore()VLexport() 函数中。您可以将这些行简化为:

    if (!VLstore("REQUEST_METHOD", cmd))
        /* error */
    if (!VLexport("REQUEST_METHOD"))
        /* error */
    
    if (!VLstore("QUERY_STRING", cmd))
        /* error */
    if (!VLexport("QUERY_STRING"))
        /* error */
    

    现在,我认为给你带来麻烦的代码:

    char *resource_str = newstr(tokenized_args[0], strlen(tokenized_args[0]));
    freelist(tokenized_args);
    

    如果rq != NULL,它出现在选择性执行的块的末尾。但是如果rq == NULL,它可以被执行,在这种情况下,tokenized_args[0] 应该是一个NULL-指针解引用。由于freelist()取消引用您传递的NULL 指针,它也会在那里爆炸。

    我认为你应该把这个例程改写成两个例程——一个处理rq == NULL 的情况,一个处理rq != NULL 的情况。如果使用rq == NULL 调用此例程是错误的,请不要尝试在rq == NULL 的情况下友好。早点死。在您的例程顶部附近添加assert(rq) 并删除冗余检查将大大简化此代码,使您可以更轻松地阅读它。

    【讨论】:

    • 感谢您的详细回复。你说得对,我认为 rq 的断言更有意义,因为它应该总是被填充并且真的应该更早地检查。我简化了代码并添加了断言。 rq 在调用 freelist() 之前不是 NULL,如果我打印 tokenized_args[2] 那么它会打印为“(null)”。我可以在那里毫无问题地调用免费,但是一旦我进入 freelist() 就会出现免费错误。就像第三个元素的值不是 null 一样。
    • 我的 C 越来越弱了;您确定cp++ 正在递增list 内的指针,而不是迭代到list 之外的下一个内存对象吗? (我,我可能会使用free(list[index++]) 只是为了确保我不会搞砸。:)
    • 它在newstr 上被证明是一个糟糕的malloc,但是谢谢,你的cmets 在帮助我编写更好的代码方面非常有帮助。
    猜你喜欢
    • 2012-02-21
    • 2013-06-27
    • 2015-03-31
    • 2017-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-07
    • 2013-08-01
    相关资源
    最近更新 更多