From 3360700b5360c5f57ee75ce177f2920b6fdccdb0 Mon Sep 17 00:00:00 2001 From: Yann Ylavic Date: Thu, 19 Apr 2018 15:01:10 +0000 Subject: [PATCH] core: Add and handle AP_GETLINE_NOSPC_EOL flag in ap_rgetline_core(). This tells the ap_getline() family of functions to consume the end of line when the buffer is exhausted. PR 62198. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1829557 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 4 ++ include/ap_mmn.h | 3 +- include/http_protocol.h | 4 +- server/protocol.c | 102 +++++++++++++++++++++++++++++++--------- 4 files changed, 88 insertions(+), 25 deletions(-) diff --git a/CHANGES b/CHANGES index 6057eec2d6..a940dada8e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,10 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.1 + *) core: Add and handle AP_GETLINE_NOSPC_EOL flag for ap_getline() family + of functions to consume the end of line when the buffer is exhausted. + PR 62198. [Yann Ylavic] + *) mod_xml2enc: Fix forwarding of error metadata/responses. PR 62180. [Micha Lenk , Yann Ylavic] diff --git a/include/ap_mmn.h b/include/ap_mmn.h index b8ed6f25ad..25cd91b900 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -572,6 +572,7 @@ * 20171014.8 (2.5.1-dev) Add CONN_STATE_NUM to enum conn_state_e * 20171014.9 (2.5.1-dev) Add response_field_size to proxy_worker_shared * 20180417.1 (2.5.1-dev) Toggle ap_filter_input|output_pending API to _NONSTD + * 20180417.2 (2.5.1-dev) Add AP_GETLINE_NOSPC_EOL flag to http_protocol.h */ #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */ @@ -579,7 +580,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20180417 #endif -#define MODULE_MAGIC_NUMBER_MINOR 1 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 2 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a diff --git a/include/http_protocol.h b/include/http_protocol.h index 4ddee84adc..f38b413b83 100644 --- a/include/http_protocol.h +++ b/include/http_protocol.h @@ -631,7 +631,9 @@ AP_DECLARE(apr_status_t) ap_get_basic_auth_components(const request_rec *r, AP_CORE_DECLARE(void) ap_parse_uri(request_rec *r, const char *uri); #define AP_GETLINE_FOLD 1 /* Whether to merge continuation lines */ -#define AP_GETLINE_CRLF 2 /*Whether line ends must be in the form CR LF */ +#define AP_GETLINE_CRLF 2 /* Whether line ends must be in the form CR LF */ +#define AP_GETLINE_NOSPC_EOL 4 /* Whether to consume up to and including the + end of line on APR_ENOSPC */ /** * Get the next line of input for the request diff --git a/server/protocol.c b/server/protocol.c index 460bf00dd9..d744ce459e 100644 --- a/server/protocol.c +++ b/server/protocol.c @@ -224,6 +224,8 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, int do_alloc = (*s == NULL), saw_eos = 0; int fold = flags & AP_GETLINE_FOLD; int crlf = flags & AP_GETLINE_CRLF; + int nospc_eol = flags & AP_GETLINE_NOSPC_EOL; + int saw_eol = 0, saw_nospc = 0; if (!n) { /* Needs room for NUL byte at least */ @@ -239,7 +241,7 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, if (last_char) *last_char = '\0'; - for (;;) { + do { apr_brigade_cleanup(bb); rv = ap_get_brigade(r->proto_input_filters, bb, AP_MODE_GETLINE, APR_BLOCK_READ, 0); @@ -283,8 +285,52 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, /* Would this overrun our buffer? If so, we'll die. */ if (n < bytes_handled + len) { - rv = APR_ENOSPC; - goto cleanup; + /* Before we die, let's fill the buffer up to its limit (i.e. + * fall through with the remaining length, if any), setting + * saw_eol on LF to stop the outer loop appropriately; we may + * come back here once the buffer is filled (no LF seen), and + * either be done at that time or continue to wait for LF here + * if nospc_eol is set. + * + * But there is also a corner cases which we want to address, + * namely if the buffer is overrun by the final LF only (i.e. + * the CR fits in); this is not really an overrun since we'll + * strip the CR finally (and use it for NUL byte), but anyway + * we have to handle the case so that it's not returned to the + * caller as part of the truncated line (it's not!). This is + * easier to consider that LF is out of counting and thus fall + * through with no error (saw_eol is set to 2 so that we later + * ignore LF handling already done here), while folding and + * nospc_eol logics continue to work (or fail) appropriately. + */ + saw_eol = (str[len - 1] == APR_ASCII_LF); + if (/* First time around */ + saw_eol && !saw_nospc + /* Single LF completing the buffered CR, */ + && ((len == 1 && ((*s)[bytes_handled - 1] == APR_ASCII_CR)) + /* or trailing CRLF overuns by LF only */ + || (len > 1 && str[len - 2] == APR_ASCII_CR + && n - bytes_handled + 1 == len))) { + /* In both cases *last_char is (to be) the CR stripped by + * later 'bytes_handled = last_char - *s'. + */ + saw_eol = 2; + } + else { + /* In any other case we'd lose data. */ + rv = APR_ENOSPC; + saw_nospc = 1; + } + len = n - bytes_handled; + if (!len) { + if (saw_eol) { + break; + } + if (nospc_eol) { + continue; + } + goto cleanup; + } } /* Do we have to handle the allocation ourselves? */ @@ -323,18 +369,28 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, /* If we got a full line of input, stop reading */ if (last_char && (*last_char == APR_ASCII_LF)) { - break; + saw_eol = 1; } + } while (!saw_eol); + + if (rv != APR_SUCCESS) { + /* End of line after APR_ENOSPC above */ + goto cleanup; } /* Now terminate the string at the end of the line; - * if the last-but-one character is a CR, terminate there */ - if (last_char > *s && last_char[-1] == APR_ASCII_CR) { - last_char--; - } - else if (crlf) { - rv = APR_EINVAL; - goto cleanup; + * if the last-but-one character is a CR, terminate there. + * LF is handled above (not accounted) when saw_eol == 2, + * the last char is CR to terminate at still. + */ + if (saw_eol < 2) { + if (last_char > *s && last_char[-1] == APR_ASCII_CR) { + last_char--; + } + else if (crlf) { + rv = APR_EINVAL; + goto cleanup; + } } bytes_handled = last_char - *s; @@ -443,22 +499,22 @@ cleanup: if (bytes_handled >= n) { bytes_handled = n - 1; } + + *read = bytes_handled; if (*s) { /* ensure the string is NUL terminated */ - (*s)[bytes_handled] = '\0'; - } - *read = bytes_handled; - - if (rv != APR_SUCCESS) { - return rv; - } + (*s)[*read] = '\0'; - /* PR#43039: We shouldn't accept NULL bytes within the line */ - if (strlen(*s) < bytes_handled) { - return APR_EINVAL; + /* PR#43039: We shouldn't accept NULL bytes within the line */ + bytes_handled = strlen(*s); + if (bytes_handled < *read) { + *read = bytes_handled; + if (rv == APR_SUCCESS) { + rv = APR_EINVAL; + } + } } - - return APR_SUCCESS; + return rv; } #if APR_CHARSET_EBCDIC -- 2.50.1