dpitch40
dpitch40

Reputation: 2691

Allocating a chunk of memory and immediately freeing it in C fails

In part of the code of a C module I am integrating with Python, I have a char** (array of strings) which is repeatedly allocated, filled with allocated strings, then freed and allocated again. The general pattern is that when a certain function is called (from Python) supplying the new contents of the array (as a list), it iterates through the array of strings, freeing each of them, then frees the array itself. It then allocates the array again to hold the contents of the new Python list, then allocates memory for each of the strings to hold.

All that to say that I am getting an error when attempting to free one of the strings in the list. This error is deterministic; it is always the same word from the same list of words at the same point in the program, but there is nothing extraordinary about that word or list of words. (It is just ["CCellEnv", "18", "34"], which is a similar format to many others) I tried adding some debug code to the loop that allocates the strings; here is the function that produces the error:

static PyObject* py_set_static_line(PyObject* self, PyObject* args)
{
    int i;
    //Free the old values of the allocated variables, if there are any
    if (numStaticWords > 0)
    {
        for (i = 0; i < numStaticWords; i++)
        {
            printf("Freeing word %d = '%s'\n", i, staticWords[i]);
            free(staticWords[i]);
        }
        free(staticWords);
        free(staticWordMatches);
    }

    //Parse arguments
    PyObject* wordList;
    unsigned short numWords;
    PyObject* wordMatchesList;
    if (!PyArg_ParseTuple(args, "O!HO!", &PyList_Type, &wordList, &numWords, &PyList_Type, &wordMatchesList))
        return NULL;

    numStaticWords = numWords;
    if (numStaticWords > 0)
    {
        staticWords = malloc(sizeof(char*) * numStaticWords);
        staticWordMatches = malloc(sizeof(int) * numStaticWords);

        PyObject* wordObj;
        PyObject* matchObj;
        char* word;
        for (i = 0; i < numStaticWords; i++)
        {
            //wordList is the list of strings passed from Python
            wordObj = PyList_GetItem(wordList, i);
            word = PyString_AsString(wordObj); //word is "18" in the failing case

            //staticWords is the char** array of strings, which has already been malloc'd
            staticWords[i] = malloc(sizeof(char) * strlen(word));

            //Test freeing the word to see if it crashes
            free(staticWords[i]); //Crashes for one specific word

            staticWords[i] = malloc(sizeof(char) * strlen(word));
            strcpy(staticWords[i], word);

            matchObj = PyList_GetItem(wordMatchesList, i);
            if (matchObj == Py_None)
            {
                staticWordMatches[i] = -1;
            }
            else
            {
                staticWordMatches[i] = PyInt_AsLong(matchObj);
            }
        }
    }
    Py_RETURN_NONE;
}

So, somehow, always and only for this specific string, allocating the memory to put it in, then immediately freeing that memory causes an error. The actual text of the string is not even copied into the memory. What could be causing this mysterious behavior?

Upvotes: 2

Views: 234

Answers (1)

alk
alk

Reputation: 70921

Here

staticWords[i] = malloc(sizeof(char) * strlen(word));
strcpy(staticWords[i], word);

you are missing to allocate the 0-termination for the "strings". So any operation on those character arrays as strings, most likely will lead to undefined behaviour.

Do it this way:

{
  int isNull = !word;

  staticWords[i] = calloc(sizeof(*staticWords[i]), (isNull ?0 :strlen(word)) + 1);
  strcpy(staticWords[i], isNull ?"" :word);
}

Upvotes: 8

Related Questions