From 399204dbae269dc9587f3b494f1c561054b02930 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 31 Oct 2022 18:58:01 +0100 Subject: [PATCH] Reject chunk size overflow, non-hex chunk size --- src/ChunkedEncoding.h | 26 +++++++++++++++++++++----- src/HttpParser.h | 6 ++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/ChunkedEncoding.h b/src/ChunkedEncoding.h index 9613780..a9b0162 100644 --- a/src/ChunkedEncoding.h +++ b/src/ChunkedEncoding.h @@ -32,6 +32,12 @@ namespace uWS { constexpr uint32_t STATE_HAS_SIZE = 0x80000000; constexpr uint32_t STATE_IS_CHUNKED = 0x40000000; constexpr uint32_t STATE_SIZE_MASK = 0x3FFFFFFF; + constexpr uint32_t STATE_IS_ERROR = 0xFFFFFFFF; + constexpr uint32_t STATE_SIZE_OVERFLOW = 0x0F000000; + + inline unsigned int chunkSize(unsigned int state) { + return state & STATE_SIZE_MASK; + } /* Reads hex number until CR or out of data to consume. Updates state. Returns bytes consumed. */ inline void consumeHexNumber(std::string_view &data, unsigned int &state) { @@ -45,10 +51,17 @@ namespace uWS { digit = (unsigned char) (digit - ('A' - ':')); } + unsigned int number = ((unsigned int) digit - (unsigned int) '0'); + + if (number > 16 || (chunkSize(state) & STATE_SIZE_OVERFLOW)) { + state = STATE_IS_ERROR; + return; + } + // extract state bits unsigned int bits = /*state &*/ STATE_IS_CHUNKED; - state = (state & STATE_SIZE_MASK) * 16u + ((unsigned int) digit - (unsigned int) '0'); + state = (state & STATE_SIZE_MASK) * 16u + number; state |= bits; data.remove_prefix(1); @@ -65,10 +78,6 @@ namespace uWS { } } - inline unsigned int chunkSize(unsigned int state) { - return state & STATE_SIZE_MASK; - } - inline void decChunkSize(unsigned int &state, unsigned int by) { //unsigned int bits = state & STATE_IS_CHUNKED; @@ -87,6 +96,10 @@ namespace uWS { return state & ~STATE_SIZE_MASK; } + inline bool isParsingInvalidChunkedEncoding(unsigned int state) { + return state == STATE_IS_ERROR; + } + /* Returns next chunk (empty or not), or if all data was consumed, nullopt is returned. */ static std::optional getNextChunk(std::string_view &data, unsigned int &state, bool trailer = false) { @@ -115,6 +128,9 @@ namespace uWS { if (!hasChunkSize(state)) { consumeHexNumber(data, state); + if (isParsingInvalidChunkedEncoding(state)) { + return std::nullopt; + } if (hasChunkSize(state) && chunkSize(state) == 2) { //printf("Setting state to trailer-parsing and emitting empty chunk\n"); diff --git a/src/HttpParser.h b/src/HttpParser.h index cb6e07b..854b970 100644 --- a/src/HttpParser.h +++ b/src/HttpParser.h @@ -481,6 +481,9 @@ private: for (auto chunk : uWS::ChunkIterator(&dataToConsume, &remainingStreamingBytes)) { dataHandler(user, chunk, chunk.length() == 0); } + if (isParsingInvalidChunkedEncoding(remainingStreamingBytes)) { + return {0, FULLPTR}; + } unsigned int consumed = (length - (unsigned int) dataToConsume.length()); data = (char *) dataToConsume.data(); length = (unsigned int) dataToConsume.length(); @@ -530,6 +533,9 @@ public: for (auto chunk : uWS::ChunkIterator(&dataToConsume, &remainingStreamingBytes)) { dataHandler(user, chunk, chunk.length() == 0); } + if (isParsingInvalidChunkedEncoding(remainingStreamingBytes)) { + return FULLPTR; + } data = (char *) dataToConsume.data(); length = (unsigned int) dataToConsume.length(); } else {