weird memory error in C
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.
精彩评论