What is wrong with my malloc/realloc here? - c

Update: Per the feedback below which I thought I understood, I've amended the code as follows but it is still troublesome:
unsigned int count = 0;
char* filebuffer;
filebuffer = malloc(sizeof(char));
if (!filebuffer)
{
error(500);
return false;
}
while (fread(filebuffer, sizeof(char), 1, file) == 1)
{
count++;
filebuffer = realloc(filebuffer, count * sizeof(char));
printf("%lu\n", (count + 1) * sizeof(char));
}
if (feof(file))
{
*content = filebuffer;
*length = count;
}
Below is some code which is meant to go through a file which is piped through to the function by popen (it's a php file), and store it into a buffer, and then give content* the same pointer and *length the number of bytes read.
However it's not working. Valgrind says:
==7608== Conditional jump or move depends on uninitialised value(s)
==7608== at 0x4C31FCE: strstr (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7608== by 0x4036C0: interpret (server.c:513)
==7608== by 0x401D66: main (server.c:259)
==7608== Uninitialised value was created by a heap allocation
==7608== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7608== by 0x4C2CF1F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7608== by 0x40418C: load (server.c:662)
==7608== by 0x403672: interpret (server.c:502)
==7608== by 0x401D66: main (server.c:259)
The code is:
unsigned int count = 0;
char* filebuffer;
filebuffer = malloc(sizeof(char));
if (!filebuffer)
{
printf("oh noes\n");
error(500);
return false;
}
while (fread(filebuffer, sizeof(char), 1, file) == 1)
{
count++;
filebuffer = realloc(NULL, sizeof(filebuffer) + sizeof(char));
}
if (feof(file))
{
*content = filebuffer;
*length = count;
}
Any feedback welcome and thanks in advance.

The argument to realloc is wrong.
sizeof(filebuffer) is equal to sizeof(char*). It does not evaluate to the the size of the array allocated.
You need to keep track of the size using another variable and use that variable. count seems to be that variable but it's not clear from your code what you are doing and what those variables stand for.
Also, when you use
filebuffer = realloc(NULL, some_size);
it is equivalent to
filebuffer = malloc(some_size);
which leads to a lot of leaked memory. To stop the memory leaks, you need to use
filebuffer = realloc(filebuffer, some_size);

Your realloc does not take the buffer of previously allocated one, also you need to track the the size of the buffer.
filebuffer = realloc(NULL, sizeof(filebuffer) + sizeof(char));
It should be
filebuffer = realloc(filebuffer, <the new size>);
But filebuffer = malloc(sizeof(char)); is just looks as bad as it is, you are allocating ONE byte each type. If you don't know the size of the file in advance, I suggest you allocate block by block.
#define BLOCKSIZE 1024
char* filebuffer;
size_t current;
filebuffer = malloc(BLOCKSIZE);
current = BLOCKSIZE;
// in the loop
filebuffer = realloc(filebuffer, BLOCKSIZE + current);
current = BLOCKSIZE + current;

Related

Resizing dynamic arrays in c

How do you properly resize an array using realloc so that the newly allocated array can have the data from the previous array plus the newly received data
int receiver (int soc_desc, char * buffer)
{
char *arr;
size_t received =0 , total_received=0;
char buff[MAX+1];
memset(buff , 0 , MAX+1);
while (1)
{
received = recv(soc_desc, buff , MAX , 0 );
if (received <= 0 )
break;
else
{
total_received = total_received + strlen(buff);
buffer = realloc(buffer, total_received);
printf("Total: %ld received: %ld \n",total_received , received);
strcat(buffer, buff);
}
printf("%s\n",buff);
}
printf("Final result: %s \n", buffer);
in this function, we pass a socket descriptor and a char *buffer = malloc(MAX) we receive data and add it to the allocated buffer and then try to reallocate the buffer for the next chunk of data, is there a way to resize the original mallocd buffer so that I can fit more characters in it without creating a new pointer for realloc each time it is called
when I compile and run this code with valgrind I get
==13850== Address 0x4a5c0e3 is 0 bytes after a block of size 3 alloc'd
==13850== at 0x483DFAF: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==13850== by 0x109884: ??? (in /home/User/Desktop/test)
==13850== by 0x109476: ??? (in /home/User/Desktop/test)
==13850== by 0x48870B2: (below main) (libc-start.c:308)
.
.
.
.
==13850== HEAP SUMMARY:
==13850== in use at exit: 27 bytes in 1 blocks
==13850== total heap usage: 22 allocs, 22 frees, 15,807 bytes allocated
since buffer is a function parameter I used free(buffer) outside the function
Compile using -g3 -ggdb3 -Wall -Wextra flags to get line number of error in valgrind and other sort of warnings.
Also, realloc() copies previous data to new one.
Some Points:
total_received = total_received + strlen(buff); can be written as total_received += strlen(buff);
You need to take buffer as char **buffer and then de-reference it like (*buffer), so that modification of buffer can be done permanently in another function's scope
"%zu" is valid format specifier for size_t
Make sure that buffer is heap-allocated in its definition scope
memset(buff , 0 , MAX+1); can be written as char buff[MAX+1] = {};
I don't see any use of arr variable in receiver() function
NOTE: Make sure that new size for buffer is larger than the previous size
Always check whether heap allocation was successful or not, by checking the pointer against NULL, eg., if(!buffer) { /* error */ }
Pass buffer like &buffer [give address of buffer to receiver() function]
received is unsigned long int AKA size_t which means it starts from 0, hence checking for less than 0 is not required, instead check for (received == 0)
Use memcpy() to append buff to *buffer by limiting the length of buff
Append null terminating character at the very end of *buffer
Final Code:
int receiver(int soc_desc, char **buffer)
{
char *arr; // idk
size_t received = 0, total_received = 0;
char buff[MAX + 1] = {}; // every element is now 0
while (1)
{
received = recv(soc_desc, buff, MAX, 0);
if (received == 0)
break;
else
{
total_received += received;
(*buffer) = realloc(*buffer, total_received + 1);
if (*buffer == NULL) // error occurred
{
exit(EXIT_FAILURE);
}
printf("Total: %zu received: %zu\n", total_received, received);
memcpy(*buffer, buff, received);
(*buffer)[total_received + 1] = 0; // nul terminating character
}
printf("%s\n", buff);
}
printf("Final result: %s \n", *buffer);
/*your rest of the code */
According to the documentation:
Reallocates the given area of memory. It must be previously allocated by malloc(), calloc() or realloc() and not yet freed with a call to free or realloc. Otherwise, the results are undefined.
The reallocation is done by either:
a) expanding or contracting the existing area pointed to by ptr, if possible. The contents of the area remain unchanged up to the lesser of the new and old sizes. If the area is expanded, the contents of the new part of the array are undefined. (*)
b) allocating a new memory block of size new_size bytes, copying memory area with size equal the lesser of the new and the old sizes, and freeing the old block.
If there is not enough memory, the old memory block is not freed and null pointer is returned. (#)
The formatting (bold text) and (*) and (#) have been added, and were not in the quoted text.
Solving (#):
bool realloc_buffer(void **buffer, size_t new_size)
{
void *tmp = *buffer;
*buffer = realloc(*buffer, new_size);
if (!*buffer) { // Realloc failed: restore the old pointer.
*buffer = tmp;
return false;
}
return true;
}
Then in your code:
int receiver (int soc_desc, char **buffer)
{
// ...
if (!realloc_buffer(buffer, old_size + total_received + 1)) { // You should know the old size
// Handle failure
}
}
You said:
... so that the newly allocated array can have the data from the previous array plus the newly received data
According to (*), you have to manually append the new data.
int receiver (int soc_desc, char **buffer)
{
// ...
char *copy = malloc(sizeof(char*) * (old_size+1));
// Make a copy of the old buffer
memcpy(copy, buffer, old_size+1);
if (!realloc_buffer(buffer, old_size + total_received + 1)) { // You should know the old size
// Handle failure
} else {
memcpy(buffer + old_size * sizeof(char*), copy, total_received+1);
}
free(copy);
// ...
}

Pointer within structure reallocs fine, but pointer to a pointer within structure fails to realloc with invalid pointer error

While working on a program which requires frequent memory allocation I came across behaviour I cannot explain. I've implemented a work around but I am curious to why my previous implementation didn't work. Here's the situation:
Memory reallocation of a pointer works
This may not be best practice (and if so please let me knwow) but I recall that realloc can allocate new memory if the pointer passed in is NULL. Below is an example where I read file data into a temporary buffer, then allocate appropriate size for *data and memcopy content
I have a file structure like so
typedef struct _my_file {
int size;
char *data;
}
And the mem reallocation and copy code like so:
// cycle through decompressed file until end is reached
while ((read_size = gzread(fh, buf, sizeof(buf))) != 0 && read_size != -1) {
// allocate/reallocate memory to fit newly read buffer
if ((tmp_data = realloc(file->data, sizeof(char *)*(file->size+read_size))) == (char *)NULL) {
printf("Memory reallocation error for requested size %d.\n", file->size+read_size);
// if memory was previous allocated but realloc failed this time, free memory!
if (file->size > 0)
free(file->data);
return FH_REALLOC_ERROR;
}
// update pointer to potentially new address (man realloc)
file->data = tmp_data;
// copy data from temporary buffer
memcpy(file->data + file->size, buf, read_size);
// update total read file size
file->size += read_size;
}
Memory reallocation of pointer to pointer fails
However, here is where I'm confused. Using the same thought that reallocation of a NULL pointer will allocate new memory, I parse a string of arguments and for each argument I allocate a pointer to a pointer, then allocate a pointer that is pointed by that pointer to a pointer. Maybe code is easier to explain:
This is the structure:
typedef struct _arguments {
unsigned short int options; // options bitmap
char **regexes; // array of regexes
unsigned int nregexes; // number of regexes
char *logmatch; // log file match pattern
unsigned int limit; // log match limit
char *argv0; // executable name
} arguments;
And the memory allocation code:
int i = 0;
int len;
char **tmp;
while (strcmp(argv[i+regindex], "-logs") != 0) {
len = strlen(argv[i+regindex]);
if((tmp = realloc(args->regexes, sizeof(char **)*(i+1))) == (char **)NULL) {
printf("Cannot allocate memory for regex patterns array.\n");
return -1;
}
args->regexes = tmp;
tmp = NULL;
if((args->regexes[i] = (char *)malloc(sizeof(char *)*(len+1))) == (char *)NULL) {
printf("Cannot allocate memory for regex pattern.\n");
return -1;
}
strcpy(args->regexes[i], argv[i+regindex]);
i++;
}
When I compile and run this I get a run time error "realloc: invalid pointer "
I must be missing something obvious but after not accomplishing much trying to debug and searching for solutions online for 5 hours now, I just ran two loops, one counts the numbers of arguments and mallocs enough space for it, and the second loop allocates space for the arguments and strcpys it.
Any explanation to this behaviour is much appreciated! I really am curious to know why.
First fragment:
// cycle through decompressed file until end is reached
while (1) {
char **tmp_data;
read_size = gzread(fh, buf, sizeof buf);
if (read_size <= 0) break;
// allocate/reallocate memory to fit newly read buffer
tmp_data = realloc(file->data, (file->size+read_size) * sizeof *tmp_data );
if ( !tmp_data ) {
printf("Memory reallocation error for requested size %d.\n"
, file->size+read_size);
if (file->data) {
free(file->data)
file->data = NULL;
file->size = 0;
}
return FH_REALLOC_ERROR;
}
file->data = tmp_data;
// copy data from temporary buffer
memcpy(file->data + file->size, buf, read_size);
// update total read file size
file->size += read_size;
}
Second fragment:
unsigned i; // BTW this variable is already present as args->nregexes;
for(i =0; strcmp(argv[i+regindex], "-logs"); i++) {
char **tmp;
tmp = realloc(args->regexes, (i+1) * sizeof *tmp );
if (!tmp) {
printf("Cannot allocate memory for regex patterns array.\n");
return -1;
}
args->regexes = tmp;
args->regexes[i] = strdup( argv[i+regindex] );
if ( !args->regexes[i] ) {
printf("Cannot allocate memory for regex pattern.\n");
return -1;
}
...
return 0;
}
A few notes:
the syntax ptr = malloc ( CNT * sizeof *ptr); is more robust than the sizeof(type) variant.
strdup() does exactly the same as your malloc+strcpy()
the for(;;) loop is less error prone than a while() loop with a loose i++; at the end of the loop body. (it also makes clear that the loopcondition is never checked)
to me if ( !ptr ) {} is easyer to read than if (ptr != NULL) {}
the casts are not needed and sometimes unwanted.

Incorrect checksum for freed object on malloc

I get an
malloc: *** error for object 0x1001012f8: incorrect checksum for freed object
- object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
error in the following function:
char* substr(const char* source, const char* start, const char* end) {
char *path_start, *path_end, *path;
int path_len, needle_len = strlen(start);
path_start = strcasestr(source, start);
if (path_start != NULL) {
path_start += needle_len;
path_end = strcasestr(path_start, end);
path_len = path_end - path_start;
path = malloc(path_len + 1);
strncpy(path, path_start, path_len);
path[path_len] = '\0';
} else {
path = NULL;
}
return path;
}
How can I make this work? When I rewrite the function to allocate the memory using path[path_len + 1] it works just fine.
Now, the part I don't understand is, that I never even call free in any point of my application, as every allocated memory is needed for the program until it exists (which, AFAIK will invalidate every allocated memory anyway?!)
So, how can a freed object be corrupt if I never free one?
The function is called in this one:
char *read_response(int sock) {
int bytes_read;
char *buf = (char*)malloc(BUF_SIZE);
char *cur_position = buf;
while ((bytes_read = read(sock, cur_position, BUF_SIZE)) > 0) {
cur_position += bytes_read;
buf = realloc(buf, sizeof(buf) + BUF_SIZE);
}
int status = atoi(substr(buf, "HTTP/1.0 ", " "));
There is the realloc, am I using that wrong? I want to read the complete server response, so I have to reallocate after every iteration, don't I?
In read_response, you are probably overwriting the end of the buffer pointed to by buf.
The problem is that buf is a pointer, so sizeof(buf) will return the size of a pointer (probably 4 or 8 depending on your CPU). You are using sizeof as if buf were an array, which is not really the same thing as a pointer in C although they seem interchangeable in some contexts.
Instead of using sizeof, you need to be keeping track of the last size that you allocated for buf, and add BUF_SIZE to that each time you enlarge the buffer.
You should also consider that the read operation may be returning considerably fewer characters than BUF_SIZE on each call, so doing a realloc on buf in each iteration may be overkill. That probably won't cause any problems for you in terms of correctness, though; it will just use more memory than it needs to.
I would do something more like the code below.
#define MIN_BUF_SPACE_THRESHOLD (BUF_SIZE / 2)
char *read_response(int sock) {
int bytes_read;
char *buf = (char*)malloc(BUF_SIZE);
int cur_position = 0;
int space_left = BUF_SIZE;
if (buf == NULL) {
exit(1); /* or try to cope with out-of-memory situation */
}
while ((bytes_read = read(sock, buf + cur_position, space_left)) > 0) {
cur_position += bytes_read;
space_left -= bytes_read;
if (space_left < MIN_BUF_SPACE_THRESHOLD) {
buf = realloc(buf, cur_position + space_left + BUF_SIZE);
if (buf == NULL) {
exit(1); /* or try to cope with out-of-memory situation */
}
space_left += BUF_SIZE;
}
}
This version has the advantage of not trying to allocate more space if the read call comes back with only a few bytes of data.
This line
buf = realloc(buf, sizeof(buf) + BUF_SIZE);
is wrong. All reallocations are with the same size, BUF_SIZE + sizeof(char*). Then you are writing to unallocated memory when reading from the socket, overwriting memory previously freed by a realloc.
You have to keep track of the allocated size,
size_t current_buf_size = BUF_SIZE;
/* ... */
char *temp = realloc(buf, current_buf_size + BUF_SIZE);
if (temp == NULL) {
/* die or repair */
}
buf = temp;

C - Unable to free allocated memory

I have a problem with an application I'm currently developing. In this program I have to read huge amounts (billions) of data from text files and manage them consequently, but since it's a two students project, the reading part will be developed by my mate. For testing reason I wrote a small procedures that generates pseudo-random structures to replace what my mate will do.
The problem is the following: a big amount of the generated data (due to redundancy) can be discarded in order to free its memory. But even invoking the free() function the memory usage keeps growing. So I tried to develop a debug application that simply generates a chunk of data and immediately frees it. And repeats that for thousands of times. Well, I can't grasp the reason, but the memory allocated to the process grows to ~1.8 GB ram and then crashes. Why? The strangest thing, that makes me thing there's a lot I'm not understanding well, is that when the process crashes the malloc does NOT return a NULL pointer, because the process always crashes when readCycles == 6008 and bypasses the NULL check.
I already read other related topics here on StackOverflow and I understood why free() doesn't reduce the memory allocated to my process. That's fine. But why the memory usage keeps growing? Shouldn't the malloc allocate previously freed memory instead of constantly requesting new one?
This is the most relevant part of my code:
#define NREAD 1000
#define READCYCLES 10000
#define N_ALPHA_ILLUMINA 7
#define N_ALPHA_SOLID 5
#define SEQLEN 76
typedef struct{
char* leftDNA;
char* leftQuality;
unsigned long int leftRow;
char* rightDNA;
char* rightQuality;
unsigned long int rightRow;
} MatePair;
unsigned long int readCycles = 0;
MatePair* readStream(MatePair* inputStream, short* eof, unsigned long int* inputSize){
double r;
unsigned long int i, j;
unsigned long int leftRow;
int alphabet[] = {'A', 'C', 'G', 'T', 'N'};
inputStream = (MatePair*) malloc (sizeof(MatePair) * (NREAD + 1));
printf("%d\n", readCycles);
if (inputStream == NULL){
(*eof) = 1;
return;
}
for (i = 0; i < NREAD; i++){
leftRow = readCycles * NREAD + i;
inputStream[i].leftDNA = (char*) malloc (SEQLEN);
inputStream[i].rightDNA = (char*) malloc (SEQLEN);
inputStream[i].leftQuality = (char*) malloc (SEQLEN);
inputStream[i].rightQuality = (char*) malloc (SEQLEN);
for (j = 0; j < SEQLEN; j++){
r = rand() / (RAND_MAX + 1);
inputStream[i].leftDNA[j] = alphabet[(int)(r * 5)];
inputStream[i].rightDNA[j] = alphabet[(int)(r * 5)];
inputStream[i].leftQuality[j] = (char) 64 + (int)(r * 60);
inputStream[i].rightQuality[j] = (char) 64 + (int)(r * 60);
}
inputStream[i].leftDNA[SEQLEN - 1] = '\0';
inputStream[i].rightDNA[SEQLEN - 1] = '\0';
inputStream[i].leftQuality[SEQLEN - 1] = '\0';
inputStream[i].rightQuality[SEQLEN - 1] = '\0';
inputStream[i].leftRow = leftRow;
inputStream[i].rightRow = leftRow;
}
inputStream[i].leftRow = -1;
readCycles++;
(*inputSize) = NREAD;
(*eof) = readCycles > READCYCLES;
return inputStream;
}
int main(int argc, char* argv[]){
short eof = 0;
unsigned long int inputSize = 0;
MatePair* inputStream = NULL;
while (!eof){
inputStream = readStream(inputStream, &eof, &inputSize);
free(inputStream);
inputStream = NULL;
}
return 0;
}
I forgot to mention that, but before posting here, instead of calling free(inputStream), I tried invoking freeMemory(inputStream). Not sure if it's the correct way of doing it, though.
void freeMemory(MatePair* memblock){
for ( ; memblock->leftRow != 1; memblock++){
free(memblock -> leftDNA);
free(memblock -> leftQuality);
free(memblock -> rightDNA);
free(memblock -> rightQuality);
}
}
Memory leaks. How many 'malloc()' you have called, how many 'free()' you must use to free all allocated memory on the heap.
Thus,
inputStream[i].leftDNA = (char*) malloc (SEQLEN);
inputStream[i].rightDNA = (char*) malloc (SEQLEN);
inputStream[i].leftQuality = (char*) malloc (SEQLEN);
inputStream[i].rightQuality = (char*) malloc (SEQLEN);
these 'malloc()' functions must be paired with free().
You're not freeing all members allocated within the read loop, hence you're losing memory eahc time. Remember, you have to free everything you allocate with a malloc, not just your array.
Ok, Just look at your edit, and your freeMemory is still wrong. Try this;
void freeMemory(MatePair* inputStream)
{
for (i = 0; i < NREAD; i++){
free(inputStream[i].leftDNA);
free(inputStream[i].leftQuality);
free(inputStream[i].rightDNA);
free(inputStream[i].rightQuality);
}
free (inputStream);
}
Your free(memblock) was in the loop, which it shouldn't have been, and I'd tend to use the same iteration sequence on freeing as mallocing. You also need to error check after each malloc, and decide what to do with a NULL at that point.

realloc()ing memory for a buffer used in recv()

I need to recv() data from a socket and store it into a buffer, but I need to make sure get all of the data so I have things in a loop. So to makes sure I don't run out of room in my buffer, I'm trying to use realloc to resize the memory allocated to the buffer. So far I have:
// receive response
int i = 0;
int amntRecvd = 0;
char *pageContentBuffer = (char*) malloc(4096 * sizeof(char));
while ((amntRecvd = recv(proxySocketFD, pageContentBuffer + i, 4096, 0)) > 0) {
i += amntRecvd;
realloc(pageContentBuffer, 4096 + sizeof(pageContentBuffer));
}
However, this doesn't seem to be working properly since Valgrind is complaining "valgrind: the 'impossible' happened:". Any advice as to how this should be done properly?
Thanks,
Hristo
update... I realized I was using realloc incorrectly. Here is a revised version:
int i = 0;
int amntRecvd = 0;
char *pageContentBuffer = (char*) malloc(4096 * sizeof(char));
while ((amntRecvd = recv(proxySocketFD, pageContentBuffer + i, 4096, 0)) > 0) {
i += amntRecvd;
char *temp = realloc(pageContentBuffer, 4096 + sizeof(pageContentBuffer));
if (temp != NULL) {
pageContentBuffer = temp;
}
}
However, valgrind still is complaining:
==25812== Syscall param socketcall.recvfrom(buf) points to unaddressable byte(s)
==25812== at 0x33B880DAA1: recv (in /lib64/libpthread-2.5.so)
==25812== by 0x401D78: tunnelURL (proxy.c:371)
==25812== by 0x40142A: client_thread (proxy.c:194)
==25812== by 0x33B8806616: start_thread (in /lib64/libpthread-2.5.so)
==25812== by 0x33B7CD3C2C: clone (in /lib64/libc-2.5.so)
==25812== Address 0x5642768 is 0 bytes after a block of size 4,104 alloc'd
==25812== at 0x4A0590B: realloc (vg_replace_malloc.c:306)
==25812== by 0x401D47: tunnelURL (proxy.c:373)
==25812== by 0x40142A: client_thread (proxy.c:194)
==25812== by 0x33B8806616: start_thread (in /lib64/libpthread-2.5.so)
==25812== by 0x33B7CD3C2C: clone (in /lib64/libc-2.5.so)
Aside from what #whirlwind said, there's also a second issue:
sizeof does not return the amount of memory previously allocated, it is actually a compile-time construct which is equivalent to sizeof(char *), i.e. the size of a character pointer.
You will need to keep track of the length of your buffer manually in a variable. There is no standard way to "ask" how much memory has been allocated by malloc/realloc.
Perhaps there is an issue because you are misusing realloc(). You need to see if it returns a new pointer, and if so, store that pointer.
// receive response
int i = 0;
int amntRecvd = 0;
char *pageContentBuffer = (char*) malloc(4096 * sizeof(char));
while ((amntRecvd = recv(proxySocketFD, pageContentBuffer + i, 4096, 0)) > 0) {
i += amntRecvd;
pageContentBuffer = realloc(pageContentBuffer, 4096 + sizeof(pageContentBuffer));
}
Look up realloc.
sizeof is a compile time value, not runtime.
It is possible for realloc to return 0.
Try this...
// receive response
int i = 0;
int amntRecvd = 0;
int currentSize = 4096;
int oldSize = currentSize;
char *pageContentBuffer = (char*) malloc(currentSize);
while ((amntRecvd = recv(proxySocketFD, pageContentBuffer + i, 4096, 0)) > 0) {
i += amntRecvd;
oldSize = currentSize;
currentSize += 4096;
char *newBuffer = malloc(currentSize);
memcpy(newBuffer,pageContentBuffer,oldSize);
free(pageContentBuffer);
pageContentBuffer = newBuffer;
}
Your best bet is to reallocate, copy and then free the memory explicitly -- realloc is quirky.
Your main problem is that you're reallocing the wrong amount of memory. You want
realloc(pageContentBuffer, 4096 + i);
sizeof(pageContentBuffer) is just sizeof(char *), which means you're reallocing far less than you need for the second read.

Resources