user994165
user994165

Reputation: 9512

Invalid Pointer: free()

In process_query_str on line 4 I call splitline and get back a string array of 3 elements, the last of which is a NULL. The array is malloced and so are the individual strings. Then I try to free each of the elements and then the array itself (towards the end of the first function). The first two strings are freed but when it goes to free the third element, which is NULL, I get the error at the bottom regarding invalid pointer. Apparently it's not getting set to NULL and should be. I tried placing breakpoints but the program won't stop.

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");
}

The stack backtrace information I get from the glibc is:

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]

Upvotes: 1

Views: 401

Answers (3)

user994165
user994165

Reputation: 9512

It turned out to be a malloc problem where I didn't allocate enough space in newstr(). The last array element caused the error when I tried to free it.

Upvotes: 2

sarnold
sarnold

Reputation: 104110

I have a feeling that there is over-defensive programming here and some opportunity for breaking apart some functions.

Consider this:

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 is being used for two different things -- one of them very short-lived. Why are you checking for rq != NULL through the alias query_str? Why not test rq != NULL directly? Note that has_query == 1 will only ever be true IFF rq != NULL.

One of has_query == 1 or query_str != NULL is redundant and should be removed entirely.

Consider this:

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

Probably this logging should be included into the VLstore() and VLexport() functions. You could simplify these lines to:

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

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

And now, the code that I think is giving you trouble:

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

It comes at the end of a block that was selectively executed if rq != NULL. But it can also be executed if rq == NULL, in which case, tokenized_args[0] should be a NULL-pointer dereference. Since freelist() will also dereference the NULL pointer you passed, it will blow up there, too.

I think you should re-write this routine into two routines -- one to handle the case rq == NULL and one to handle the case rq != NULL. Don't try to be friendly in the case rq == NULL if it is a mistake to call this routine with rq == NULL. Die early. Adding assert(rq) near the top of your routine and removing the redundant checks would drastically simplify this code to the point where you could more easily read it.

Upvotes: 0

Jonathan Leffler
Jonathan Leffler

Reputation: 755064

This isn't an answer to your problem, but it will help you reduce the volume of code. You have fragments such as:

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

This would be simpler if you were able to write:

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

You can do that with an adaptation of this function:

#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);
}

You could declare it in a header with:

#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);

Then GCC will spot misuses of format conversion specifications vs arguments passed to the function.

Note that this version of logg() is essentially call-compatible with the previous one. The only time there'd be a discrepancy is if a string being printed contained a %Z-like string that is now treated as a conversion specification and was not before. You should, though, use logg("%s\n", argument); or similar.

Upvotes: 2

Related Questions