From 914e49b9b7375b22c15fd2ae9a3c3da44841e5ac Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 23 Sep 2023 11:20:00 +0200 Subject: [PATCH] lib: let the max filesize option stop too big transfers too Previously it would only stop them from getting started if the size is known to be too big then. Update the libcurl and curl docs accordingly. Fixes #11810 Reported-by: Elliot Killick Assisted-by: Jay Satiro Closes #11820 --- docs/cmdline-opts/max-filesize.d | 9 ++++++--- docs/libcurl/opts/CURLOPT_MAXFILESIZE.3 | 3 +++ docs/libcurl/opts/CURLOPT_MAXFILESIZE_LARGE.3 | 3 +++ lib/c-hyper.c | 6 +++++- lib/file.c | 4 +++- lib/ldap.c | 4 +++- lib/mqtt.c | 4 +++- lib/progress.c | 9 ++++++++- lib/progress.h | 5 ++++- lib/smb.c | 7 ++++++- lib/telnet.c | 5 +++-- lib/tftp.c | 7 +++++-- lib/transfer.c | 4 +++- 13 files changed, 55 insertions(+), 15 deletions(-) diff --git a/docs/cmdline-opts/max-filesize.d b/docs/cmdline-opts/max-filesize.d index ada730d926..7541a27127 100644 --- a/docs/cmdline-opts/max-filesize.d +++ b/docs/cmdline-opts/max-filesize.d @@ -18,6 +18,9 @@ A size modifier may be used. For example, Appending 'k' or 'K' counts the number as kilobytes, 'm' or 'M' makes it megabytes, while 'g' or 'G' makes it gigabytes. Examples: 200K, 3m and 1G. (Added in 7.58.0) -**NOTE**: The file size is not always known prior to download, and for such -files this option has no effect even if the file transfer ends up being larger -than this given limit. +**NOTE**: before curl 8.4.0, when the file size is not known prior to +download, for such files this option has no effect even if the file transfer +ends up being larger than this given limit. + +Starting with curl 8.4.0, this option aborts the transfer if it reaches the +threshold during transfer. diff --git a/docs/libcurl/opts/CURLOPT_MAXFILESIZE.3 b/docs/libcurl/opts/CURLOPT_MAXFILESIZE.3 index f4b024047f..c1ff168693 100644 --- a/docs/libcurl/opts/CURLOPT_MAXFILESIZE.3 +++ b/docs/libcurl/opts/CURLOPT_MAXFILESIZE.3 @@ -41,6 +41,9 @@ transfers this option has no effect - even if the file transfer eventually ends up being larger than this given limit. If you want a limit above 2GB, use \fICURLOPT_MAXFILESIZE_LARGE(3)\fP. + +Since 8.4.0, this option also stops ongoing transfers if they reach this +threshold. .SH DEFAULT None .SH PROTOCOLS diff --git a/docs/libcurl/opts/CURLOPT_MAXFILESIZE_LARGE.3 b/docs/libcurl/opts/CURLOPT_MAXFILESIZE_LARGE.3 index 7e37fca92a..c40989e760 100644 --- a/docs/libcurl/opts/CURLOPT_MAXFILESIZE_LARGE.3 +++ b/docs/libcurl/opts/CURLOPT_MAXFILESIZE_LARGE.3 @@ -40,6 +40,9 @@ returned. The file size is not always known prior to the download start, and for such transfers this option has no effect - even if the file transfer eventually ends up being larger than this given limit. + +Since 8.4.0, this option also stops ongoing transfers if they reach this +threshold. .SH DEFAULT None .SH PROTOCOLS diff --git a/lib/c-hyper.c b/lib/c-hyper.c index 47dadfe4ce..c558955c9d 100644 --- a/lib/c-hyper.c +++ b/lib/c-hyper.c @@ -258,7 +258,11 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk) } data->req.bytecount += len; - Curl_pgrsSetDownloadCounter(data, data->req.bytecount); + result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount); + if(result) { + data->state.hresult = result; + return HYPER_ITER_BREAK; + } return HYPER_ITER_CONTINUE; } diff --git a/lib/file.c b/lib/file.c index c751e8861a..ffa9fb76d0 100644 --- a/lib/file.c +++ b/lib/file.c @@ -571,7 +571,9 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) if(result) return result; - Curl_pgrsSetDownloadCounter(data, bytecount); + result = Curl_pgrsSetDownloadCounter(data, bytecount); + if(result) + return result; if(Curl_pgrsUpdate(data)) result = CURLE_ABORTED_BY_CALLBACK; diff --git a/lib/ldap.c b/lib/ldap.c index 33a4dea0a8..239d3fbf01 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -735,7 +735,9 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) if(result) goto quit; dlsize++; - Curl_pgrsSetDownloadCounter(data, dlsize); + result = Curl_pgrsSetDownloadCounter(data, dlsize); + if(result) + goto quit; } if(ber) diff --git a/lib/mqtt.c b/lib/mqtt.c index eb53560f9a..5cb2d24110 100644 --- a/lib/mqtt.c +++ b/lib/mqtt.c @@ -668,7 +668,9 @@ MQTT_SUBACK_COMING: mq->npacket -= nread; k->bytecount += nread; - Curl_pgrsSetDownloadCounter(data, k->bytecount); + result = Curl_pgrsSetDownloadCounter(data, k->bytecount); + if(result) + goto end; /* if QoS is set, message contains packet id */ diff --git a/lib/progress.c b/lib/progress.c index 6092b782c7..e783a9c86d 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -317,9 +317,16 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, /* * Set the number of downloaded bytes so far. */ -void Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size) +CURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size) { + if(data->set.max_filesize && (size > data->set.max_filesize)) { + failf(data, "Exceeded the maximum allowed file size " + "(%" CURL_FORMAT_CURL_OFF_T ")", + data->set.max_filesize); + return CURLE_FILESIZE_EXCEEDED; + } data->progress.downloaded = size; + return CURLE_OK; } /* diff --git a/lib/progress.h b/lib/progress.h index 0049cd04be..fc39e34d20 100644 --- a/lib/progress.h +++ b/lib/progress.h @@ -46,7 +46,10 @@ int Curl_pgrsDone(struct Curl_easy *data); void Curl_pgrsStartNow(struct Curl_easy *data); void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size); void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size); -void Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size); + +/* It is fine to not check the return code if 'size' is set to 0 */ +CURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size); + void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size); void Curl_ratelimit(struct Curl_easy *data, struct curltime now); int Curl_pgrsUpdate(struct Curl_easy *data); diff --git a/lib/smb.c b/lib/smb.c index afcc99de24..32c5137a44 100644 --- a/lib/smb.c +++ b/lib/smb.c @@ -1049,7 +1049,12 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done) } data->req.bytecount += len; data->req.offset += len; - Curl_pgrsSetDownloadCounter(data, data->req.bytecount); + result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount); + if(result) { + req->result = result; + next_state = SMB_CLOSE; + break; + } next_state = (len < MAX_PAYLOAD_SIZE) ? SMB_CLOSE : SMB_DOWNLOAD; break; diff --git a/lib/telnet.c b/lib/telnet.c index 850f88c1ec..836e255c9d 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -1570,8 +1570,9 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) } total_dl += nread; - Curl_pgrsSetDownloadCounter(data, total_dl); - result = telrcv(data, (unsigned char *)buf, nread); + result = Curl_pgrsSetDownloadCounter(data, total_dl); + if(!result) + result = telrcv(data, (unsigned char *)buf, nread); if(result) { keepon = FALSE; break; diff --git a/lib/tftp.c b/lib/tftp.c index 8ed1b887b4..e78140d520 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -1141,12 +1141,15 @@ static CURLcode tftp_receive_packet(struct Curl_easy *data) result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)state->rpacket.data + 4, state->rbytes-4); + if(!result) { + k->bytecount += state->rbytes-4; + result = Curl_pgrsSetDownloadCounter(data, + (curl_off_t) k->bytecount); + } if(result) { tftp_state_machine(state, TFTP_EVENT_ERROR); return result; } - k->bytecount += state->rbytes-4; - Curl_pgrsSetDownloadCounter(data, (curl_off_t) k->bytecount); } break; case TFTP_EVENT_ERROR: diff --git a/lib/transfer.c b/lib/transfer.c index d0602b8753..4c9ff0aa84 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -671,7 +671,9 @@ static CURLcode readwrite_data(struct Curl_easy *data, k->bytecount += nread; max_recv -= nread; - Curl_pgrsSetDownloadCounter(data, k->bytecount); + result = Curl_pgrsSetDownloadCounter(data, k->bytecount); + if(result) + goto out; if(!k->chunk && (nread || k->badheader || is_empty_data)) { /* If this is chunky transfer, it was already written */