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
openFilehas a return-type ofvoid, yet you check for a return-value here:Secondly, as Peter also points out, you’re not allocating enough space when calling
realloc:On the first iteration, where
(*fileDataLen) == 0, after incrementing*fileDataLen, you now only have a value of1, which means that you aren’t reallocating anything (i.e, you’re simply passing back the memory thatfileDatawas already pointing to since it hasn’t changed the size of the allocated array). Therefore the next time you callfileData[(*fileDataLen)] = currFile;during another recursive call, you are going to be copying the value ofcurrFileintofileData[1], but that memory hasn’t been allocated yet. Furthermore, the next-time thatreallocis called, it may not longer reallocate memory at the same location, sofileDatawill be pointing to a completely different location, with only the first array entry copied over.Third, you can’t call
free(fileData)infunc1()since you, by callingreallocinside yourfunc2()function, have changed the value of where the memory is pointing, and you are not passing the actual memory address for the originalfileDatavariable by reference to yourfunc2()function. In other words if the call tomalloc()infunc1()returned a value of let’s say 0x10000, and you calledreallocon that allocated memory somewhere else in the code, the memory that was allocated at 0x10000 has now moved somewhere else, but the value offileDatain the context of the local scope offunc1()is still 0x10000. Thus when you effectively callfree(0x10000), which is what’s happening when you callfree(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 tofunc2(), or passfileDataby reference, meaning the function signature offunc2()andopenFile()will need to change to afileInfo***type, and you’ll also need an extra layer of indirection whenever accessingfileDatainfunc2()andopenFile(). Then when you callreallocanywhere else, you are actually modifying the value offileDataas it was allocated infunc1()as well, and can callfree()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 allocatedfileInfonodes on the heap sincefileDatawas only an array of pointers to the nodes, not the actual nodes themselves.