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.
This commit is contained in:
Yang Tse 2011-09-25 18:53:29 +02:00
parent e276802ff8
commit 119f43360b
8 changed files with 50 additions and 5 deletions

View 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 cause writing to this connection to become paused. See
\fIcurl_easy_pause(3)\fP for further details. \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 This function may be called with zero bytes data if the transferred file is
empty. empty.

View File

@ -685,4 +685,5 @@ CURL_VERSION_SPNEGO 7.10.8
CURL_VERSION_SSL 7.10 CURL_VERSION_SSL 7.10
CURL_VERSION_SSPI 7.13.2 CURL_VERSION_SSPI 7.13.2
CURL_VERSION_TLSAUTH_SRP 7.21.4 CURL_VERSION_TLSAUTH_SRP 7.21.4
CURL_WRITEFUNC_OUT_OF_MEMORY 7.22.1
CURL_WRITEFUNC_PAUSE 7.18.0 CURL_WRITEFUNC_PAUSE 7.18.0

View File

@ -187,10 +187,15 @@ typedef int (*curl_progress_callback)(void *clientp,
#define CURL_MAX_HTTP_HEADER (100*1024) #define CURL_MAX_HTTP_HEADER (100*1024)
#endif #endif
/* This is a magic return code for the write callback that, when returned, /* This is a magic return code for the write callback that, when returned,
will signal libcurl to pause receiving on the current transfer. */ will signal libcurl to pause receiving on the current transfer. */
#define CURL_WRITEFUNC_PAUSE 0x10000001 #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, typedef size_t (*curl_write_callback)(char *buffer,
size_t size, size_t size,
size_t nitems, size_t nitems,

View File

@ -354,6 +354,8 @@ static CURLcode ftp_pl_insert_finfo(struct connectdata *conn,
return CURLE_OK; return CURLE_OK;
} }
/* Curl_ftp_parselist is a write callback function */
size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
void *connptr) void *connptr)
{ {
@ -365,6 +367,10 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
unsigned long i = 0; unsigned long i = 0;
CURLcode rc; 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 */ if(parser->error) { /* error in previous call */
/* scenario: /* scenario:
* 1. call => OK.. * 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 * 3. (last) call => is skipped RIGHT HERE and the error is hadled later
* in wc_statemach() * in wc_statemach()
*/ */
if(parser->error == CURLE_OUT_OF_MEMORY)
return CURL_WRITEFUNC_OUT_OF_MEMORY;
return bufflen; 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(); parser->file_data = Curl_fileinfo_alloc();
if(!parser->file_data) { if(!parser->file_data) {
parser->error = CURLE_OUT_OF_MEMORY; parser->error = CURLE_OUT_OF_MEMORY;
return bufflen; return CURL_WRITEFUNC_OUT_OF_MEMORY;
} }
parser->file_data->b_data = malloc(FTP_BUFFER_ALLOCSIZE); parser->file_data->b_data = malloc(FTP_BUFFER_ALLOCSIZE);
if(!parser->file_data->b_data) { if(!parser->file_data->b_data) {
PL_ERROR(conn, CURLE_OUT_OF_MEMORY); PL_ERROR(conn, CURLE_OUT_OF_MEMORY);
return bufflen; return CURL_WRITEFUNC_OUT_OF_MEMORY;
} }
parser->file_data->b_size = FTP_BUFFER_ALLOCSIZE; parser->file_data->b_size = FTP_BUFFER_ALLOCSIZE;
parser->item_offset = 0; 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->file_data = NULL;
parser->error = CURLE_OUT_OF_MEMORY; parser->error = CURLE_OUT_OF_MEMORY;
PL_ERROR(conn, CURLE_OUT_OF_MEMORY); PL_ERROR(conn, CURLE_OUT_OF_MEMORY);
return bufflen; return CURL_WRITEFUNC_OUT_OF_MEMORY;
} }
} }

View 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; writeit = data->set.fwrite_rtp?data->set.fwrite_rtp:data->set.fwrite_func;
wrote = writeit(ptr, 1, len, data->set.rtp_out); 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) { if(CURL_WRITEFUNC_PAUSE == wrote) {
failf (data, "Cannot pause RTP"); failf (data, "Cannot pause RTP");
return CURLE_WRITE_ERROR; return CURLE_WRITE_ERROR;

View File

@ -459,6 +459,11 @@ CURLcode Curl_client_write(struct connectdata *conn,
wrote = len; 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) if(CURL_WRITEFUNC_PAUSE == wrote)
return pausewrite(data, type, ptr, len); 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) */ regardless of the ftp transfer mode (ASCII/Image) */
wrote = writeit(ptr, 1, len, data->set.writeheader); 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) if(CURL_WRITEFUNC_PAUSE == wrote)
/* here we pass in the HEADER bit only since if this was body as well /* 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, then it was passed already and clearly that didn't trigger the pause,

View 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 size_t cb = size * nmemb;
const char *end = (char*)ptr + cb; 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)) { if(cb > 20 && checkprefix("Content-disposition:", str)) {
const char *p = str + 20; 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); len = (ssize_t)cb - (p - str);
filename = parse_filename(p, len); filename = parse_filename(p, len);
/* TODO: OOM handling - return (size_t)-1 ? */
if(filename) { if(filename) {
outs->filename = filename; outs->filename = filename;
outs->alloc_filename = TRUE; outs->alloc_filename = TRUE;
break; break;
} }
else
return CURL_WRITEFUNC_OUT_OF_MEMORY;
} }
} }

View 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; 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) { if(!out->stream) {
out->bytes = 0; /* nothing written yet */ out->bytes = 0; /* nothing written yet */
if(!out->filename) { if(!out->filename) {