Pregunta

I have a C code (first C code I have ever written), and there is an error in it, but I dont know, where. When I try to free a variable (dinamically allocated, its name is out_html) I get double free or corruption. I have no idea why my program does this, I checked all my calls for free etc.

The code:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <math.h>

#include "fcntl.h"
#include "errno.h"

#include "sys/types.h"
#include "sys/stat.h"

#include "unistd.h"

#define MAX_SIZE 512

typedef struct node
{
    char* data;
    struct node * nextnode;
} node;

int max(int a, int b)
{
    if(a>b) return a;
    else return b;
}

node* push(node * stackTop, char* data)
{
    //static 
    node* newItem;

    newItem = calloc(sizeof(node),1);
    newItem->data = data;
    newItem->nextnode = stackTop;

    return newItem;
}

node* pop(node* stackTop)
{
    if(stackTop != NULL)
    {
        free(stackTop->data);
        node* P = stackTop;
        return stackTop->nextnode;
        free(P);
    }else return NULL;
}

int isMajorTag(char* tag)
{
    if(strcmp(tag, "<html>") == 0 || strcmp(tag, "</html>") == 0 ||
       strcmp(tag, "<body>") == 0 || strcmp(tag, "</body>") == 0 ||
       strcmp(tag, "<head>") == 0 || strcmp(tag, "</head>") == 0 ) { return 1; }
    else                         { return 0; };
}

int isHTMLtag(char* tag, char* tags)
{
    char* tag2;
    if(strstr(tag," ") != NULL)
    {
        char* strptr = strstr(tag, " ");
        int End = strptr - tag;
        char* tag_ = strndup(tag, End);
        tag2 = calloc((strlen(tag_) + strlen("*") + 2), sizeof(char));
        strcpy(tag2, tag_);
        strcat(tag2,"*");
        free(tag_); 
    } 
    else tag2 = tag;
    int ret;
    if(strstr(tags, tag2) != NULL){ ret =  1; }
    else                         { ret = 0; };
    if(tag2 != tag ) free(tag2); 
    return ret;
}

int isCloserTagOf(char* cltag, char* tag)
{
    int ret = 1;
    if( cltag[1] != '/' ) ret = 0;
    if( tag[1] == '/' ) ret = 0;
    char* ntag;
    char* ncltag;
    if(strstr(tag," ") != NULL)
    {
        char* strptr = strstr(tag, " ");
        int End = strptr - tag;
        ntag = strndup(tag, End) + 1;
        // ntag = calloc(strlen(ntag0) + 1 + 1, sizeof(char)); strcpy(ntag, ntag0); strcat(ntag, ">");
        ncltag = strndup(cltag+2,strlen(cltag) - 3);
    } else
    {
        ntag = tag + 1;
        ncltag = cltag + 2;
    }
//  printf("|%s|%s|  %i", ntag, ncltag, strcmp(ncltag, ntag));

    if(strcmp(ncltag, ntag) != 0) ret = 0;
    return ret;
}

int isIndividualTag(char* tag)
{
    if(strcmp(tag,"</br>") == 0) return 1;
    else if(strncmp(tag,"<!--#include file=",18) == 0) return 2;
    else if(strncmp(tag,"<!--#echo var=",14) == 0) return 3;
    else if(strncmp(tag,"<!--",4) == 0) return 4;
    else return 0;
}

int main(int argc,char *argv[])
{
    char* fname;
    if(argc == 2)
    {
        fname = argv[1];
    } else
    {
        printf("Give me a filename!");
        fname = calloc( MAX_SIZE, sizeof(char));
        scanf("%s", fname);
    };
    printf("Parameter: %s \n\n", fname);

// beolvasas
    int f = open(fname, O_RDONLY);
    long pos = lseek(f, 0, SEEK_END);
    lseek(f, 0, SEEK_SET);

    char *buff = calloc(pos,1);
    read(f, buff, pos);
    close(f);

    f = open("valid-tags", O_RDONLY);
    pos = lseek(f, 0, SEEK_END);
    lseek(f, 0, SEEK_SET);

    char *valids = calloc(pos,1);
    read(f, valids, pos);
    close(f);

//  printf("File: %s %s %i   ",buff, valids, isCloserTagOf("</html>","<html>")); printf("Igen? %i", isHTMLtag("</head>",valids));

    node* Stack = NULL;
    char *P = buff;
    int is_valid = 1;
    int bodyCnt = 0;
    char* body[6];
    int correct_body = 1;
    char* out_html = calloc(strlen(buff), sizeof(char));
    while(P[0] != '\0' )
    {
        if(P[0] == '<')
        {
            char* strptr = strstr(P, ">");
            if(strptr != NULL)
            {
                int nextCloser = strptr - P + 1;
                char* tag = strndup(P, nextCloser);
                int IsIndividual = isIndividualTag(tag);
                if(isHTMLtag(tag, valids) || IsIndividual)
                {
                    if(IsIndividual)
                    { 
                        if(IsIndividual == 2) // file inclusion
                        {
                            char* firstQ = strstr(tag, "\"");
                            char* secondQ;
                            if( firstQ ) secondQ = strstr(firstQ + 1, "\"");

                            if( firstQ && secondQ )
                            {
                                char* incl_filename = strndup((firstQ + 1), (secondQ - firstQ - 1));
                                f = open(incl_filename, O_RDONLY);
                                pos = lseek(f, 0, SEEK_END);
                                lseek(f, 0, SEEK_SET);
                                char *inclstr = calloc(pos,1);
                                read(f, inclstr, pos);
                                close(f);
                                char* new_out_html = calloc((max(strlen(buff),strlen(out_html)) + pos + 1 + 1 + 1), sizeof(char));
                                strcpy(new_out_html, out_html);
                                strcat(new_out_html, inclstr);
                                free(out_html); out_html = NULL; // free(inclstr);
                                out_html = new_out_html; 
                            } else
                            {   
                                printf("Invalid file inclusion! \n");
                                is_valid = 0; break;
                            };
                        } else if (IsIndividual == 3) // date time
                        {
                            time_t t = time(NULL);                  
                        //  int nDigits = floor(log10(abs(t)) + 1; (strlen(out_html) + nDigits
                            char* timestring = ctime(&t);
                            char* new_out_html = calloc(1 + max(strlen(buff),strlen(out_html)) + strlen(timestring), sizeof(char));
                            strcpy(new_out_html, out_html);
                            strcat(new_out_html, timestring);
                            //printf("%s",new_out_html);
                            free(out_html); out_html = NULL; // free(timestring);
                            out_html = new_out_html;  
                        } else
                        {
                            strcat(out_html, tag);
                        };
                    }else
                    {
                        strcat(out_html, tag);
                        if(Stack != NULL && isCloserTagOf(tag,Stack->data))
                        {
                            Stack = pop(Stack);
                        }else
                        {
                            Stack = push(Stack, tag);
                        };
                    } 
                    if(isMajorTag(tag))
                    {   
                        if(bodyCnt < 6)
                        {   body[bodyCnt] = calloc(strlen(tag), sizeof(char));
                            strcpy(body[bodyCnt],tag);
                            ++bodyCnt;
                        }else
                        {   
                            printf("Too much major html tag found...");
                            correct_body = 0;
                        }
                    }
                }else
                {
                    printf("Invalid html tag: %s \n", tag);
                    is_valid = 0;
                    break;
                }
                P = P + nextCloser;
            } 
            else
            {
                printf("Unclosed tag\n");
                is_valid = 0;
                break;
            }
        } else
        {  //printf("-%c",P[0]);
           strncat(out_html, P,1); 
        //  printf("{(%s)}",out_html);
           P = P + 1;
        };
    };

    int i;
    char* correctBody[] = { "<html>", "<head>", "</head>", "<body>", "</body>", "</html>"};
    for(i = 0; i < bodyCnt && correct_body; ++i) {
            correct_body = (strcmp(body[i],correctBody[i]) == 0); }
    if(is_valid && Stack == NULL && 
       correct_body && bodyCnt == 6){ printf("\nValid HTML Code\n");
                                      printf("\n\n%s\n",out_html);
                                        }
    else                              printf("\nInvalid.\n");

//  printf("%i %i %i",bodyCnt,correct_body,is_valid);
    /*****************************************************************/
    for(i=0;i<bodyCnt;++i) free(body[i]);
    free(buff); free(valids); // 
    if(out_html != NULL) free(out_html);
    return 0;
}

At the end of the code: if(out_html != NULL) free(out_html); Without this, there is no crash.

I think the crash is caused somewhere near line 196.

(there must be a valig-html file with proper html tags - without this, the code is useless, I mean a file like this: )

¿Fue útil?

Solución

The error message can be a bit confusing.

When allocation say 200 bytes with calloc, the routine internally allocates a tad more:
say 8 , 16 or 32 bytes to make linked lists and other bookkeeping (i.e. has it been freed).

If the strings that are appended or copied with strcpy/strcat do not fit the target array, it internally leads to possible corruption of the bookkeeping.

So the error doesn't necessarily do anything with freeing a pointer twice.

Otros consejos

It is hard to figure out what's going on in your code, but some potentically nonsensical operations are visible at the first sight. For example, consider this sequence

int f = open(fname, O_RDONLY);
long pos = lseek(f, 0, SEEK_END);
lseek(f, 0, SEEK_SET);

char *buff = calloc(pos,1);
read(f, buff, pos);
close(f);
...

char* out_html = calloc(strlen(buff), sizeof(char));

You read contents of some file into an allocated buffer buff (the size of the buffer is exactly the size of the file). And later you treat buff as a null-terminated string: you use it as an argument of strlen.

But you never bothered to null-teriminate your buff! How is this supposed to work? If it is not null-terminated, it is not a string and it cannot be meaningfully used as an argument of strlen.

You program contains several instances of code that follows the same pattern: the entire contents of some file is read into a buffer of the exact size and then interpreted as a null-terminated string, while in reality it is not null-terminated (nobody bothered to ensure null-termination).

Is the terminating zero supposed to be present in the file itself? If so, then how are we supposed to know that? We are not telepathes here.

This looks wrong: (BTW: I could not find a definition / declaration of stackTop)

node* pop(node* stackTop)
{
    if(stackTop != NULL)
    {
        free(stackTop->data);
        node* P = stackTop;
        return stackTop->nextnode;
        free(P);
    }else return NULL;
}

Here is another calloc() that's too short. (the strcpy() will put its nul byte at a place that does not belong to the calloc()ed object. BTW: sizeof(char) is 1, by definition.

                    if(bodyCnt < 6)
                    {   body[bodyCnt] = calloc(strlen(tag), sizeof(char));
                        strcpy(body[bodyCnt],tag);
                        ++bodyCnt;
                    }else
                    {   
                        printf("Too much major html tag found...");
                        correct_body = 0;
                    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top