From 86d9a61be6395220714b1a50d5144e65668961f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernst=20Sj=C3=B6strand?= Date: Tue, 21 Dec 2021 11:05:22 +0000 Subject: [PATCH] Fix buffer overflow in url parser and add test Reference: https://git.gnunet.org/libmicrohttpd.git/commit/?id=a110ae6276660bee3caab30e9ff3f12f85cf3241 Upstream-Status: Backport CVE: CVE-2021-3466 Signed-off-by: Ernst Sjöstrand --- src/microhttpd/postprocessor.c | 18 ++++++-- src/microhttpd/test_postprocessor.c | 66 +++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/src/microhttpd/postprocessor.c b/src/microhttpd/postprocessor.c index b7f6b10..ebd1686 100644 --- a/src/microhttpd/postprocessor.c +++ b/src/microhttpd/postprocessor.c @@ -137,8 +137,7 @@ struct MHD_PostProcessor void *cls; /** - * Encoding as given by the headers of the - * connection. + * Encoding as given by the headers of the connection. */ const char *encoding; @@ -586,7 +585,7 @@ post_process_urlencoded (struct MHD_PostProcessor *pp, pp->state = PP_Error; break; case PP_Callback: - if ( (pp->buffer_pos + (end_key - start_key) > + if ( (pp->buffer_pos + (end_key - start_key) >= pp->buffer_size) || (pp->buffer_pos + (end_key - start_key) < pp->buffer_pos) ) @@ -636,6 +635,11 @@ post_process_urlencoded (struct MHD_PostProcessor *pp, { if (NULL == end_key) end_key = &post_data[poff]; + if (pp->buffer_pos + (end_key - start_key) >= pp->buffer_size) + { + pp->state = PP_Error; + return MHD_NO; + } memcpy (&kbuf[pp->buffer_pos], start_key, end_key - start_key); @@ -663,6 +667,11 @@ post_process_urlencoded (struct MHD_PostProcessor *pp, last_escape); pp->must_ikvi = false; } + if (PP_Error == pp->state) + { + /* State in error, returning failure */ + return MHD_NO; + } return MHD_YES; } @@ -1424,7 +1433,8 @@ MHD_destroy_post_processor (struct MHD_PostProcessor *pp) the post-processing may have been interrupted at any stage */ if ( (pp->xbuf_pos > 0) || - (pp->state != PP_Done) ) + ( (pp->state != PP_Done) && + (pp->state != PP_Init) ) ) ret = MHD_NO; else ret = MHD_YES; diff --git a/src/microhttpd/test_postprocessor.c b/src/microhttpd/test_postprocessor.c index 2c37565..cba486d 100644 --- a/src/microhttpd/test_postprocessor.c +++ b/src/microhttpd/test_postprocessor.c @@ -451,6 +451,71 @@ test_empty_value (void) } +static enum MHD_Result +value_checker2 (void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *filename, + const char *content_type, + const char *transfer_encoding, + const char *data, + uint64_t off, + size_t size) +{ + return MHD_YES; +} + + +static int +test_overflow () +{ + struct MHD_Connection connection; + struct MHD_HTTP_Header header; + struct MHD_PostProcessor *pp; + size_t i; + size_t j; + size_t delta; + char *buf; + + memset (&connection, 0, sizeof (struct MHD_Connection)); + memset (&header, 0, sizeof (struct MHD_HTTP_Header)); + connection.headers_received = &header; + header.header = MHD_HTTP_HEADER_CONTENT_TYPE; + header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED; + header.header_size = strlen (header.header); + header.value_size = strlen (header.value); + header.kind = MHD_HEADER_KIND; + for (i = 128; i < 1024 * 1024; i += 1024) + { + pp = MHD_create_post_processor (&connection, + 1024, + &value_checker2, + NULL); + buf = malloc (i); + if (NULL == buf) + return 1; + memset (buf, 'A', i); + buf[i / 2] = '='; + delta = 1 + (MHD_random_ () % (i - 1)); + j = 0; + while (j < i) + { + if (j + delta > i) + delta = i - j; + if (MHD_NO == + MHD_post_process (pp, + &buf[j], + delta)) + break; + j += delta; + } + free (buf); + MHD_destroy_post_processor (pp); + } + return 0; +} + + int main (int argc, char *const *argv) { @@ -463,6 +528,7 @@ main (int argc, char *const *argv) errorCount += test_multipart (); errorCount += test_nested_multipart (); errorCount += test_empty_value (); + errorCount += test_overflow (); if (errorCount != 0) fprintf (stderr, "Error (code: %u)\n", errorCount); return errorCount != 0; /* 0 == pass */