I'm writing a program in C that checks for circular symbolic links. The strategy is to create a struct fileInfo:
typedef struct fileInfo fileInfo;
struct fileInfo {
ino_t inode;
dev_t devID;
};
that will store a file's inode and devID. We create an array of these structs and check every time before opening a new file whether the file already exists. If so, then it's a circular link.
void func1(...)
{
fileInfo **fileData = malloc(sizeof(struct fileInfo*));
int fileDataLen = 0;
char* path = "path of file";
/* some flags */
func2(path, fileData, &fileDataLen);
for (int i = 0; i < fileDataLen; i++)
free(fileData[i]);
free(fileData);
}
void func2(char* path, fileInfo ** fileData, int * fileDataLen)
{
//try to open file
struct stat buf;
if (openFile(file, &buf, followSymLinks) == -1)
exit(1);
fileData = checkForLoops(fileData, fileDataLen, &buf, file);
if (S_ISDIR(buf.st_mode))
{
char* newPath = /* modify path */
func2(newPath,fileData, fileDataLen);
}
/* other stuff */
}
int openFile(char* file, struct stat * buf, fileInfo ** fileData, int * fileDataLen)
{
if (lstat(path, buf) < 0)
{
fprintf(stderr, "lstat(%s) failed\n", path);
return -1;
}
return 0;
}
fileInfo** checkForLoops(fileInfo **fileData, int * fileDataLen,struct stat *buf,
char* path)
{
for (int i = 0; i < (*fileDataLen); i++)
{
if (fileData[i]->inode == buf->st_ino &&
fileData[i]->devID == buf->st_dev)
fprintf(stderr, "circular symbolic link at %s\n", path);
}
fileInfo *currFile = malloc(sizeof(struct fileInfo));
memcpy(&currFile->inode, &buf->st_ino, sizeof(buf->st_ino));
memcpy(&currFile->devID, &buf->st_dev, sizeof(buf->st_dev));
fileData[(*fileDataLen)] = currFile;
(*fileDataLen)++;
fileData = realloc(fileData, ((*fileDataLen)+1) * sizeof(struct fileInfo*));
return fileData;
}
I notice, however, that after a few calls to func2(), there is a memory leak and fileData points to nothing. I'm just not sure where the leak is coming from, since I don't free anything in func2(). I'm assuming there are some realloc shenanigans, but I don't understand why. Help would be greatly appreciated!
I'm noticing a couple oddities in the code.
First, the function signature of openFile has a return-type of void, yet you check for a return-value here:
if (openFile(file, &buf, fileData, fileDataLen) < 0)
Secondly, as Peter also points out, you're not allocating enough space when calling realloc:
fileData = realloc(fileData, (*fileDataLen) * sizeof(struct fileInfo*));
On the first iteration, where (*fileDataLen) == 0, after incrementing *fileDataLen, you now only have a value of 1, which means that you aren't reallocating anything (i.e, you're simply passing back the memory that fileData was already pointing to since it hasn't changed the size of the allocated array). Therefore the next time you call fileData[(*fileDataLen)] = currFile; during another recursive call, you are going to be copying the value of currFile into fileData[1], but that memory hasn't been allocated yet. Furthermore, the next-time that realloc is called, it may not longer reallocate memory at the same location, so fileData will be pointing to a completely different location, with only the first array entry copied over.
Third, you can't call free(fileData) in func1() since you, by calling realloc inside your func2() function, have changed the value of where the memory is pointing, and you are not passing the actual memory address for the original fileData variable by reference to your func2() function. In other words if the call to malloc() in func1() returned a value of let's say 0x10000, and you called realloc on that allocated memory somewhere else in the code, the memory that was allocated at 0x10000 has now moved somewhere else, but the value of fileData in the context of the local scope of func1() is still 0x10000. Thus when you effectively call free(0x10000), which is what's happening when you call free(fileData), you are going to get an error since the memory for the array is no longer allocated at 0x10000. In order to free the array, you are either going to have to return the updated pointer to the pointer array from all the recursive calls to func2(), or pass fileData by reference, meaning the function signature of func2() and openFile() will need to change to a fileInfo*** type, and you'll also need an extra layer of indirection whenever accessing fileData in func2() and openFile(). Then when you call realloc anywhere else, you are actually modifying the value of fileData as it was allocated in func1() as well, and can call free() on that pointer.
Finally, keep in mind that if you only free the memory allocated for fileData, you are going to have a big memory leak for all the allocated fileInfo nodes on the heap since fileData was only an array of pointers to the nodes, not the actual nodes themselves.
Your problem is that you aren't allocating enough memory for fileData:
fileInfo *fileData = malloc(sizeof(struct fileInfo));
Here you only allocate memory for a single pointer to fileInfo, instead of the array of fileInfo instance you seem to be using.
Sorry, my first idea was wrong... but your problem still seems to be that you aren't allocating enough memory for fileData - just in a different place:
fileData[(*fileDataLen)] = currFile; // 1
(*fileDataLen)++;
fileData = realloc(fileData, (*fileDataLen) * sizeof(struct fileInfo*)); // 2
Here you allocate one element less than needed. You start with fileDataLen of 0, and fileData containing 1 element. After opening the first file, you increment fileDataLen to 1, then reallocate the array to contain 1 element instead of 2! Thus, when opening the 2nd file, your buffer is overrun at // 1 above, and some memory is overwritten.
You should keep this invariant at all times, reallocating the array to a size fileDataLen + 1:
fileData = realloc(fileData, (*fileDataLen + 1) * sizeof(struct fileInfo*));
I don't know what kind of processing you're performing over path variable in func2, but you might be trying to modify a static string, which will lead you to another memory problem since those kind of strings are stored in a private memory zone reserved by the OS.
Related
This question already has answers here:
Why doesn't free(p) set p to NULL?
(9 answers)
Closed 5 years ago.
After I call free on a struct variable in C, and then check if that variable is NULL, it shows me that it is NOT NULL. So free did not work? My code:
struct main_struct {
struct my_file *file;
};
struct my_file {
char *host;
int port; // this cannot be malloc'ed and free'ed
};
struct my_file* read_file() {
FILE *f;
struct my_file *file;
//open FILE for reading..
file = malloc(sizeof(struct my_file)); //allocate memory for my_file struct
memset(file, 0, sizeof(struct my_file));
// while loop to read file content..
// in the loop:
char *value = line;
file->host = malloc(sizeof(char) * strlen(value)); //allocate memory for host member variable
strncpy(file->host, value, strlen(value)); //assign value to host variable
return file;
}
int main() {
struct main_struct *mstr;
mstr = malloc(sizeof(struct main_struct)); //allocate memory to main_struct
memset(mstr, 0, sizeof(struct main_struct));
mstr->my_file = read_file(); //call to read file, allocate mem for my_file struct and the 'host' member variable
// some code
// call free here:
if(mstr->my_file->host != NULL) {
free(mstr->my_file->host);
}
// check if mem has been freed:
if(mstr->my_file->host == NULL) {
printf("mstr->my_file->host is NULL, good.\n");
} else {
printf("mstr->my_file->host is NOT NULL, bad.\n"); // I see this.
}
// I also try to free mstr->my_file:
if(mstr->my_file != NULL) {
free(mstr->my_file);
}
// check if mem has been freed:
if(mstr->my_file == NULL) {
printf("mstr->my_file is NULL, good.\n");
} else {
printf("mstr->my_file is NOT NULL, bad.\n"); // I see this.
}
// and also mstr itself..
}
Am I using the free function correctly, because I have seen examples where free has been called like this:
free(&mystruct->myfile->host); by sending the address of the pointer to free. But I think that the way I am calling free now, is correct..?
free(x) doesn't set x no NULL automatically, it just deallocates the memory and leaves x pointing to an invalid location. If you want to free x you can use a function like
void clear(void** ptr) { free(*ptr); *ptr = NULL; }
...
free(&(mstr->my_file->host));
Or you can do it manually each time. The comma operator can help here:
mstr->my_file->host = (free(mstr->my_file->host), NULL);
Edit: if you happen to be using glib (and its memory management wrappers), there is g_clear_pointer and g_clear_object to help with this.
free(&foo) is always wrong. You can only free pointer values that were returned from malloc / calloc / realloc (and wrappers such as strdup). &foo is the address of an existing variable (managed by the compiler).
free(ptr) will not set ptr to NULL. In general, a function call f(x) cannot modify x because C passes arguments by value. It will simply release the memory behind ptr, without touching ptr itself.
free(ptr) is a bit of a special case, because afterwards the value of ptr is indeterminate, which means your if(mstr->my_file->host != NULL) check actually has undefined behavior (looking at an indeterminate value is not allowed).
See also http://c-faq.com/malloc/ptrafterfree.html.
Random comments:
Never use strncpy. It is not a string function (in the sense that it doesn't work with or produce C strings), and its behavior will bite you at some point.
Multiplying by sizeof (char) is pointless: sizeof (char) is 1 by definition.
malloc + memset can be combined by using calloc to get zero-initialized memory. (In some cases calloc is also much faster than malloc/memset.)
I'm creating a source files containing buffer functionality that I want to use for my other library that I'm creating.
It is working correctly but I'm having trouble getting rid of the buffer structure that I'm creating in one of the functions. The following snippets should help illustrate my problem:
C header:
//dbuffer.h
...
typedef struct{
char *pStorage;
int *pPosition;
int next_position;
int number_of_strings;
int total_size;
}DBUFF;
...
C source:
//dbuffer.c
...
DBUFF* dbuffer_init(char *init_pArray)
{
//Find out how many elements the array contains
int size = sizeof_pArray(init_pArray);
//Initialize buffer structure
DBUFF *buffer = malloc(sizeof(DBUFF));
//Initialize the storage
buffer->pStorage = malloc( (sizeof(char)) * (size) );
strncpy( &(buffer->pStorage)[0] , &init_pArray[0] , size);
buffer->number_of_strings = 1;
buffer->total_size = size;
buffer->next_position = size; //size is the next position because array allocates elements from 0 to (size-1)
//Initialize the position tracker which keeps record of starting position for each string
buffer->pPosition = malloc(sizeof(int) * buffer->number_of_strings );
*(buffer->pPosition + (buffer->number_of_strings -1) ) = 0;
return buffer;
}
void dbuffer_destroy(DBUFF *buffer)
{
free(buffer->pStorage);
free(buffer);
}
...
Main:
#include <stdio.h>
#include <stdlib.h>
#include "dbuffer.h"
int main(int argc, char** argv)
{
DBUFF *buff;
buff = dbuffer_init("Bring the action");
dbuffer_add(buff, "Bring the apostles");
printf("BUFFER CONTENTS: ");
dbuffer_print(buff);
dbuffer_destroy(buff);
// Looks like it has been succesfully freed because output is garbage
printf("%s\n", buff->pStorage);
//Why am I still able to access struct contents after the pointer has been freed ?
printf("buff total size: %d\n", buff->total_size);
return (EXIT_SUCCESS);
}
Output:
BUFFER CONTENTS: Bring the action/0Bring the apostles/0
��/�
buff total size: 36
RUN SUCCESSFUL (total time: 94ms)
Question:
Why am I still able to access struct contents using the line below after the pointer to the struct has been freed ?
printf("buff total size: %d\n", buff->total_size);
Once you've called free() on the allocated pointer, attempt to make use of the pointer invokes undefined behavior. You should not be doing that.
To quote C11 standard, chapter §7.22.3.4, free() function
The free() function causes the space pointed to by ptr to be deallocated, that is, made
available for further allocation. [..]
It never say's anything about a cleanup, which you might be (wrongly) expecting.
Just to add clarity, calling free() does not always actually free up the allocated physical memory. It just enables that pointer (memory space) to be allocated again (returning the same pointer, for example) for successive calls to malloc() and family. After calling free(), that pointer is not supposed to be used from your program anymore but C standard does not guarantee of a cleanup of the allocated memory.
If any attempt is made to read memory that has been freed can crash your program. Or they might not. As far as the language is concerned, its undefined behaviour.
Your compiler won't warn you about it(or stop you from accessing it). But clearly don't do this after calling free -
printf("buff total size: %d\n", buff->total_size);
As a good practice you can set the freed pointer to NULL .
free() call will just mark the memory in heap as available for use. So you still have the pointer pointing to this memory location but it's not available anymore for you. Thus, the next call to malloc() is likely to assign this memory to the new reservation.
To void this situations normally once you free() the memory allocated to a pointer you should set it to NULL. De-referencing NULL is UB also but at least when debugging you can see tha pointer should not be used because it's not pointing to a valid memory address.
[too long for a comment]
To allow your "destructor" to set the pointer passed to NULL modify your code like this:
void dbuffer_destroy(DBUFF ** buffer)
{
if ((NULL == buffer) || (NULL == *buffer))
{
return;
}
free((*buffer)->pPosition);
free((*buffer)->pStorage);
free(*buffer);
*buffer = NULL;
}
and call it like this:
...
dbuffer_destroy(&buff);
...
On RHEL6, I'm facing a strange problem with realloc(). At some point in the program, realloc() returns NULL (the old pointer has an address and there's plently of memory available). What's being allocated is 200 structure elements (structure below). For some reason, when I do a realloc() instead, it works, but I then have to assign the old pointer to the new one. Below is a simplified version of my code.
This is perhaps a server tuning issue more than a programming one. What is your opinion?
Thanks.
//hearder file
typedef struct { /* Variable Node Detail Record */
long next;
long mask;
char *value;
// more stuff...
} NODETEST;
extern NODETEST *oldNodes;
extern NODETEST *newNodes;
//program
#define MAXSIZE 200
// do some stuff with oldNodes....
int alloc_nodes (void)
{
// Allocate or grow the table
oldNodes = (NODETEST *) malloc(MAXSIZE * sizeof(NODETEST));
if( oldNodes == NULL ) {
//handle exception...
exit(1);
}
//oldNodes = (NODETEST *) realloc(oldNodes,MAXSIZE * sizeof(NODETEST)); // *** FAILS
newNodes = (NODETEST *) realloc(oldNodes,MAXSIZE * sizeof(NODETEST)); // *** WORKS
if( newNodes == NULL ){
printf("errno=%d\n", errno );
}else{
oldNodes = newNodes; }
}
Your first call malloc with a size S and then realloc with the same size S. This is wrong: you have to pass to realloc the new wanted size (independently of the current size - it is not an increment). Here, there is a big chance realloc returns exactly the same pointer it received. BTW it is not clear why you want to do with a malloc immediately followed by a realloc. Gives us more detail.
If you want a dynamic table whose size auto-adjusts, you need to allocate an initial size storing its size in a variable (e.g. alloc_size) and keep the current number of occupied elements in another variable (e.g. n_elem) . When you add an element you increment this number. When the table is full reallocate it. Here is a sketch
NODETEST *newNodes = NULL;
int allocated_elem = 0;
int n_elem = 0;
#define ALLOC_INCR 200
then at each addition:
if (n_elem >= alloc_size) { // the first time realloc is as malloc since nodes == NULL
alloc_size += ALLOC_INCR;
nodes = (NODETEST *) realloc(nodes, alloc_size * sizeof(NODETEST));
if (nodes == NULL) {
//handle exception...
exit(1);
}
}
// add your element at nodes[n_elem]
n_elem++;
Recall that realloc acts like malloc when the received pointer is NULL (case of the first call). Thus it allocates the initial table. Subsequent calls reallocate it by adjusting the size with a constant increment (here 200). Some other schemes are possible for the enlargement of the table, for instance you can multiply the size by a factor (e.g. 2) starting from 32:
if (n_elem >= alloc_size) { // the first time realloc is as malloc since nodes == NULL
alloc_size = (alloc_size == 0) ? 32 : alloc_size * 2;
Regarind the FAIL and WORKS comments: it is clear that if you assign oldNodes (in the FAIL code) then newNodes is not assigned and keeps its initial value which is zero (NULL) since it is declared as a global variable and not initialized (well I suppose, it is extern here). Thus the test if (newNodes == NULL) will probably fail.
I am implementing symbol table using link list, The code works fine but there is memory leak in code,
I have following structure
struct node
{
char* pcKey;
void* pvValue;
struct node *next;
};
struct _Sym
{
int totalBindings;
struct node *node;
};
add I have sym_new method to allocate memory for sym instance
sym Sym_new (void)
{
_Sym *m_SymTable_t = (_Sym*) malloc (sizeof(_Sym));
if(m_SymTable_t == NULL)
{
return NULL;
}
else
{
m_SymTable_t->totalBindings = 0;
m_SymTable_t->node = NULL;
return m_SymTable_t;
}//endif
}
I am allocating memory for key and value in other function based on the string length.
The free method is
typedef struct _Sym *Sym;
void Sym_free (Sym m_SymTable_t)
{
assert(m_SymTable_t != NULL);
struct node* temp = m_SymTable_t->node;
struct node *currentBinding = NULL;
while(temp != NULL)
{
currentBinding = temp;
temp = temp -> next;
//Removing comment for the below line throws segfault
//free(currentBinding -> pcKey);
//free(currentBinding -> pvValue);
free(currentBinding);
}
free(m_SymTable_t);
}
What is proper way to free the sym completely?
I have uploaded my symTable_Link.cpp file at link
The variables pcKey and pvValue should probably be initialised to null in the Sym_new() function. Otherwise they may contain any old value. This is because malloc doesn't necessarily zero the memory allocated: it just allocates a chunk of memory and the memory could therefore be filled with junk.
So, if for some reason sym_put() is not called for the newly created object these pointers could point to invalid memory and upon your call to free() segfault. If you initialise them to null free() will just ignore them and won't try to free the memory.
A "hacky" DEBUG-only technique you could use to check that the pcKey and pvValue variables are definitely allocated by a sym_put call would be to initialise them in sym_new with a dummy value, for example 0xCDCDCDCD (careful about pointer-widths here... this is why I'm calling this a hacky technique). Then in sym_free check for this magic constant before freeing pcKey and pvValue. If you find it, there's the problem...
Also of interest may be the thread Do I cast the result of malloc?
EDIT:
Looked at the code linked and you appear to be discarding const!
The function id defined as:
int SymTable_put (SymTable_t m_SymTable_t, const char *pcKey, const void *pvValue)
But then does this cast...
temp->pcKey = (char*)pcKey;
temp->pvValue = (char*)pvValue;
This is a bad idea. You're "fooling" the compiler into invalidating your const promise.
THE BUG:
Ok, so you allocate as follows
temp->pcKey = (char*) malloc (sizeof(char) * strlen (pcKey));
But then you overwrite this pointer using
temp->pcKey = (char*)pcKey;
So you a) have a memory leak and b) have just stashed the wrong pointer, which is probs why you get the segfault. You you probably meant to do instead is (strdup is useful here)...
temp->pcKey = strdup(pcKey);
This will allocate new memory for the string in pcKey and COPY the string into the new memory.
I would hazzard a guess you called the function like this...
SymTable_put (xxx, "KEY string", "VALUE string");
Then your code did this
temp->pcKey = (char*)malloc (sizeof(char) * strlen (pcKey));
...
temp->pcKey = (char*)pcKey;
So now temp->pcKey points to "KEY string" itself and not a copy of it. So when you try to free the string constant, your program complains. What you want to do is copy the string from pcKey into temp->pcKey instead of overwriting the pointer.
EDIT:
As per comments the mallocs need space + 1 to include the null terminator. Also sizeof(char) is always 1, so is redundant. Try strdup instread.
First, I declare variables before the main() function:
// Files
FILE *density_model_file;
char *density_model_filename;
float *density_array;
Next, I open the FILE * for reading and allocate memory for the density array:
density_model_file = open4read(density_model_filename, program_name);
density_array = allocator(density_model_size, sizeof(float));
Up to this point, the debugger shows everything is working fine. Here
is the step that I can't seem to fix, where I am attempting to load
data into the calloc'd array:
density_array = floatfromfile(sizeof(float), density_model_size, density_model_file, density_model_filename);
The density_array has a NULL value after this step for some reason.
Here is the code for this function (contained in a separate .c file).
I have bolded the part where I think the issue exists:
float * floatfromfile(unsigned long int entrysize, int numentries, FILE *inputfile, const char *filename)
{
/* Declaration of density model array size variable */
int numbytes;
**void *temparray = 0;
/* Writes the gravity model to file */
numbytes = (int)fread(temparray, entrysize, numentries, inputfile);**
/* Checks that the forward model file has a sufficient number of entries */
if (numbytes == numentries)
{
printf("loaded %i values from %s using fread()\n", numbytes, filename);
return((float *)temparray);
}
else
{
printf("ERROR: %i data points read from %s of %i needed\n", numbytes, filename, numentries);
return((float *)temparray);
}
}
Any insight would be much appreciated. I think the issue might be that calloc() returns a pointer to a void array. I can provide the other functions if needed.
You seem to have a misunderstanding about how pointers work. What you need to do is pass density_array into floatfromfile as an argument.
What you are doing instead is overwriting the pointer to your allocated memory, with the return value from floatfromfile. That return value is always NULL because that's what you assigned it to (as temparray).
fread expects to be able to write its results into an allocated memory block. But you're giving it temparray which has not been allocated—in fact its value is 0. So you're giving fread the address 0 to write into, which is likely to cause the program to crash. Instead you need to pass your allocated pointer density_array at this point.