allow write callbacks to indicate OOM to libcurl
authorYang Tse <yangsita@gmail.com>
Sun, 25 Sep 2011 16:53:29 +0000 (18:53 +0200)
committerYang Tse <yangsita@gmail.com>
Sun, 25 Sep 2011 17:05:46 +0000 (19:05 +0200)
Allow (*curl_write_callback) write callbacks to return
CURL_WRITEFUNC_OUT_OF_MEMORY to properly indicate libcurl of OOM conditions
inside the callback itself.

docs/libcurl/curl_easy_setopt.3
docs/libcurl/symbols-in-versions
include/curl/curl.h
lib/ftplistparser.c
lib/rtsp.c
lib/sendf.c
src/tool_cb_hdr.c
src/tool_cb_wrt.c

index 9f6f11d896ec440cc48b0365c86c177782aa738e..b9d42b54eb608cbfe7d144d3a33544b59964a56e 100644 (file)
@@ -160,6 +160,11 @@ From 7.18.0, the function can return CURL_WRITEFUNC_PAUSE which then will
 cause writing to this connection to become paused. See
 \fIcurl_easy_pause(3)\fP for further details.
 
+From 7.22.1, the function can return CURL_WRITEFUNC_OUT_OF_MEMORY to indicate
+libcurl that an attempt to dynamically allocate memory from within the write
+callback itself has failed. This will abort the transfer and make libcurl
+return CURLE_OUT_OF_MEMORY.
+
 This function may be called with zero bytes data if the transferred file is
 empty.
 
index 028bb1a575769a7a588060408a087c2705a9c046..5c985bde71157f186e62b0d6ab5e4942990f2817 100644 (file)
@@ -685,4 +685,5 @@ CURL_VERSION_SPNEGO             7.10.8
 CURL_VERSION_SSL                7.10
 CURL_VERSION_SSPI               7.13.2
 CURL_VERSION_TLSAUTH_SRP        7.21.4
+CURL_WRITEFUNC_OUT_OF_MEMORY    7.22.1
 CURL_WRITEFUNC_PAUSE            7.18.0
index ef6bda8f3aed6e33046a020a8110bc2edd1360d7..4f0182616839255bf325a10813178d3d5c576bb6 100644 (file)
@@ -187,10 +187,15 @@ typedef int (*curl_progress_callback)(void *clientp,
 #define CURL_MAX_HTTP_HEADER (100*1024)
 #endif
 
-
 /* This is a magic return code for the write callback that, when returned,
    will signal libcurl to pause receiving on the current transfer. */
 #define CURL_WRITEFUNC_PAUSE 0x10000001
+
+/* If the write callback itself allocates memory dynamically and this fails
+   due to an out of memory condition, returning CURL_WRITEFUNC_OUT_OF_MEMORY
+   is the proper way to tell libcurl of this condition. */
+#define CURL_WRITEFUNC_OUT_OF_MEMORY 0x10000002
+
 typedef size_t (*curl_write_callback)(char *buffer,
                                       size_t size,
                                       size_t nitems,
index bbf6e9ef915dd73e578a3547a9b9b4372aee1c2a..12ee51d86afa837607c6c14e39087876d8d18dcc 100644 (file)
@@ -354,6 +354,8 @@ static CURLcode ftp_pl_insert_finfo(struct connectdata *conn,
   return CURLE_OK;
 }
 
+/* Curl_ftp_parselist is a write callback function */
+
 size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
                           void *connptr)
 {
@@ -365,6 +367,10 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
   unsigned long i = 0;
   CURLcode rc;
 
+  if(bufflen >= CURL_WRITEFUNC_PAUSE)
+    /* CURL_WRITEFUNC_PAUSE limits input size */
+    return CURL_WRITEFUNC_OUT_OF_MEMORY;
+
   if(parser->error) { /* error in previous call */
     /* scenario:
      * 1. call => OK..
@@ -372,6 +378,9 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
      * 3. (last) call => is skipped RIGHT HERE and the error is hadled later
      *    in wc_statemach()
      */
+    if(parser->error == CURLE_OUT_OF_MEMORY)
+      return CURL_WRITEFUNC_OUT_OF_MEMORY;
+
     return bufflen;
   }
 
@@ -388,12 +397,12 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
       parser->file_data = Curl_fileinfo_alloc();
       if(!parser->file_data) {
         parser->error = CURLE_OUT_OF_MEMORY;
-        return bufflen;
+        return CURL_WRITEFUNC_OUT_OF_MEMORY;
       }
       parser->file_data->b_data = malloc(FTP_BUFFER_ALLOCSIZE);
       if(!parser->file_data->b_data) {
         PL_ERROR(conn, CURLE_OUT_OF_MEMORY);
-        return bufflen;
+        return CURL_WRITEFUNC_OUT_OF_MEMORY;
       }
       parser->file_data->b_size = FTP_BUFFER_ALLOCSIZE;
       parser->item_offset = 0;
@@ -416,7 +425,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
         parser->file_data = NULL;
         parser->error = CURLE_OUT_OF_MEMORY;
         PL_ERROR(conn, CURLE_OUT_OF_MEMORY);
-        return bufflen;
+        return CURL_WRITEFUNC_OUT_OF_MEMORY;
       }
     }
 
index 198c25dc1364560f7c39191d231b2725c16a630c..77c1367ff3cba94e1f20aac26329617ae61f5ed8 100644 (file)
@@ -725,6 +725,11 @@ CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len)
   writeit = data->set.fwrite_rtp?data->set.fwrite_rtp:data->set.fwrite_func;
   wrote = writeit(ptr, 1, len, data->set.rtp_out);
 
+  if(CURL_WRITEFUNC_OUT_OF_MEMORY == wrote) {
+    failf (data, "Out of memory writing RTP data");
+    return CURLE_OUT_OF_MEMORY;
+  }
+
   if(CURL_WRITEFUNC_PAUSE == wrote) {
     failf (data, "Cannot pause RTP");
     return CURLE_WRITE_ERROR;
index 847090be3d729d5935f36b8cd0712c3057de3b71..e9d5c37757a80545bd40d08816d66f57ebc2fb08 100644 (file)
@@ -459,6 +459,11 @@ CURLcode Curl_client_write(struct connectdata *conn,
       wrote = len;
     }
 
+    if(CURL_WRITEFUNC_OUT_OF_MEMORY == wrote) {
+      failf(data, "Out of memory writing body");
+      return CURLE_OUT_OF_MEMORY;
+    }
+
     if(CURL_WRITEFUNC_PAUSE == wrote)
       return pausewrite(data, type, ptr, len);
 
@@ -481,6 +486,12 @@ CURLcode Curl_client_write(struct connectdata *conn,
        regardless of the ftp transfer mode (ASCII/Image) */
 
     wrote = writeit(ptr, 1, len, data->set.writeheader);
+
+    if(CURL_WRITEFUNC_OUT_OF_MEMORY == wrote) {
+      failf(data, "Out of memory writing header");
+      return CURLE_OUT_OF_MEMORY;
+    }
+
     if(CURL_WRITEFUNC_PAUSE == wrote)
       /* here we pass in the HEADER bit only since if this was body as well
          then it was passed already and clearly that didn't trigger the pause,
index 2643ad2cfd964da76761697c14b15996939f42e1..6dcc9f46aefb30bdd2e2c8515a5d080f83635c08 100644 (file)
@@ -47,6 +47,10 @@ size_t tool_header_cb(void *ptr, size_t size, size_t nmemb, void *userdata)
   const size_t cb = size * nmemb;
   const char *end = (char*)ptr + cb;
 
+  if(cb >= CURL_WRITEFUNC_PAUSE)
+    /* CURL_WRITEFUNC_PAUSE limits input size */
+    return CURL_WRITEFUNC_OUT_OF_MEMORY;
+
   if(cb > 20 && checkprefix("Content-disposition:", str)) {
     const char *p = str + 20;
 
@@ -74,12 +78,13 @@ size_t tool_header_cb(void *ptr, size_t size, size_t nmemb, void *userdata)
       */
       len = (ssize_t)cb - (p - str);
       filename = parse_filename(p, len);
-      /* TODO: OOM handling - return (size_t)-1 ? */
       if(filename) {
         outs->filename = filename;
         outs->alloc_filename = TRUE;
         break;
       }
+      else
+        return CURL_WRITEFUNC_OUT_OF_MEMORY;
     }
   }
 
index 3a2cd791a925fb6fb2423a53d654695edbe4b1cd..16edd7199ca34bfc7d2903cf051300d2a7e56191 100644 (file)
@@ -51,6 +51,10 @@ size_t tool_write_cb(void *buffer, size_t sz, size_t nmemb, void *userdata)
    */
   const size_t err_rc = (sz * nmemb) ? 0 : 1;
 
+  if(sz * nmemb >= CURL_WRITEFUNC_PAUSE)
+    /* CURL_WRITEFUNC_PAUSE limits input size */
+    return CURL_WRITEFUNC_OUT_OF_MEMORY;
+
   if(!out->stream) {
     out->bytes = 0; /* nothing written yet */
     if(!out->filename) {