From 119f43360b60c903982104944145d9cd8b7ec054 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Sun, 25 Sep 2011 18:53:29 +0200 Subject: allow write callbacks to indicate OOM to libcurl 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 | 5 +++++ docs/libcurl/symbols-in-versions | 1 + include/curl/curl.h | 7 ++++++- lib/ftplistparser.c | 15 ++++++++++++--- lib/rtsp.c | 5 +++++ lib/sendf.c | 11 +++++++++++ src/tool_cb_hdr.c | 7 ++++++- src/tool_cb_wrt.c | 4 ++++ 8 files changed, 50 insertions(+), 5 deletions(-) diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index 9f6f11d89..b9d42b54e 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -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. diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index 028bb1a57..5c985bde7 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -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 diff --git a/include/curl/curl.h b/include/curl/curl.h index ef6bda8f3..4f0182616 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -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, diff --git a/lib/ftplistparser.c b/lib/ftplistparser.c index bbf6e9ef9..12ee51d86 100644 --- a/lib/ftplistparser.c +++ b/lib/ftplistparser.c @@ -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; } } diff --git a/lib/rtsp.c b/lib/rtsp.c index 198c25dc1..77c1367ff 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -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; diff --git a/lib/sendf.c b/lib/sendf.c index 847090be3..e9d5c3775 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -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, diff --git a/src/tool_cb_hdr.c b/src/tool_cb_hdr.c index 2643ad2cf..6dcc9f46a 100644 --- a/src/tool_cb_hdr.c +++ b/src/tool_cb_hdr.c @@ -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; } } diff --git a/src/tool_cb_wrt.c b/src/tool_cb_wrt.c index 3a2cd791a..16edd7199 100644 --- a/src/tool_cb_wrt.c +++ b/src/tool_cb_wrt.c @@ -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) { -- cgit v1.2.3