3

I'm having a problem with the realloc function. I'm using C only (so no vector) with LibCurl. The problem I'm having is that I'm getting the following error (realloc(): invalid next size) on the 12th iteration of the write_data function (the function I pass to Curl as a callback, it is called each time libcurl has some data to pass back (data is passed in chunks) ).

Trace:

-Removed-

Source:

#include <stdio.h>
#include <stdlib.h>
#include <curl/curl.h>
#include <string.h>

char * Data; //stores the data
size_t  RunningSize;

int write_data( char *ptr, size_t size, size_t nmemb, void *stream )
{
    size_t ThisSize = (size * nmemb); //Stores the size of the data to be stored
    size_t DataLen = strlen( Data ); //length of the data so far

    RunningSize = (RunningSize + ThisSize ); //update running size (used as new size)

   Data = realloc( Data, RunningSize ); //get new mem location (on the 12th iteration, this fails)
   strcat( Data, ptr); //add data in ptr to Data

    return ThisSize; //the function must return the size of the data it received so cURL knows things went ok.
}

int main( )
{
  CURL *curl;
  CURLcode res;
  const char * UserAgent = "";

  Data = malloc(1); //so realloc will work
  RunningSize += 1;

  curl = curl_easy_init();
  if(curl)
  {
    curl_easy_setopt( curl, CURLOPT_NOBODY, 0 );
    curl_easy_setopt( curl, CURLOPT_URL, "http://www.google.co.uk/" );
    curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, write_data);
    curl_easy_setopt( curl, CURLOPT_USERAGENT, UserAgent );
    curl_easy_setopt( curl, CURLOPT_HEADER, 1 );

    //preform request.
    res = curl_easy_perform(curl);

    //output the data (debugging purposes)
    puts( Data );

    //cleanup
    curl_easy_cleanup(curl);
    free(Data);
  }

  return 0;
}

Thanks in advance,

Kewley
  • 527
  • 1
  • 6
  • 13
  • Without seeing your source code it's difficult to know what your table means and what's actually happening. Please put relevant source code in your question. – CB Bailey May 30 '10 at 14:57
  • What you posted into `pastebin` is virtually useless. There are no calls to `write_data` in your code, so there's no way to figure out what you are doing there. – AnT stands with Russia May 30 '10 at 14:59
  • @AndreyT: It's a callback, called from inside libcurl. – Donal Fellows May 30 '10 at 15:00
  • 2
    As a side note: there's no need to do `malloc(1)` to initialize a pointer for `realloc`. `realloc` can "reallocate" a null pointer without any problems. – AnT stands with Russia May 30 '10 at 15:01
  • 4
    `realloc` is slightly fiddly to use correctly. You need to check that it succeeds by checking for a non-NULL return value, however if it does return NULL then the memory pointed to by the parameter is still allocated needs to be freed. For this reason it is a bad idea to use `x = realloc(x, n)` if `x` is you only copy of the pointer to the memory area. You need to do (something like) `new_x = realloc(x, n); if (new_x) x = new_x; else failure_action();` – CB Bailey May 30 '10 at 15:07
  • The malloc(1) initialization is needed for _some_ systems, such as SUN OS 4. – Joseph Quinsey May 30 '10 at 15:20
  • Not sure why I used pastebin if I'm honest...Thanks for the comments and tips, I'll make sure to change my code to check if realloc returns NULL. Thanks! – Kewley Jun 01 '10 at 11:48

1 Answers1

10

The data passed in to write_data() isn't necessarily nul-terminated; that's why it tells you the number of bytes.

This means you can't use strcat(). Using it is running off the end of the array and corrupting the data structures used by malloc / realloc, hence the error.

Your write_data() should use memcpy() instead, like so:

int write_data( char *ptr, size_t size, size_t nmemb, void *stream )
{
    size_t ThisSize = (size * nmemb); //Stores the size of the data to be stored
    size_t DataLen = RunningSize; //length of the data so far

    RunningSize = (RunningSize + ThisSize ); //update running size (used as new size)

    Data = realloc( Data, RunningSize ); //get new mem location (on the 12th iteration, this fails)
    memcpy((char *)Data + DataLen, ptr, ThisSize); //add data in ptr to Data

    return ThisSize; //the function must return the size of the data it received so cURL knows things went ok.
}

You will also need to initialise RunningSize to 0, not 1. You can initialise Data to NULL - passing NULL to realloc() is allowed (and makes it behave just like malloc()).

caf
  • 233,326
  • 40
  • 323
  • 462