@@ -34,15 +34,16 @@ enum {
3434 CHUNK_LENGTH , // Getting chunk length - HHHH[;...] CR LF
3535 CHUNK_EXTENSION , // Getting chunk length - HHHH[;...] CR LF
3636 CHUNK_DATA , // Handling chunk data
37+ CHUNK_ERROR , // Invalid chunk header
3738 CHUNK_END , // Getting chunk end marker - CR LF
3839};
3940
4041AsyncWebServerRequest::AsyncWebServerRequest (AsyncWebServer *s, AsyncClient *c)
4142 : _client(c), _server(s), _handler(NULL ), _response(NULL ), _onDisconnectfn(NULL ), _temp(), _parseState(PARSE_REQ_START ), _version(0 ), _method(HTTP_ANY ),
4243 _url(), _host(), _contentType(), _boundary(), _authorization(), _reqconntype(RCT_HTTP ), _authMethod(AsyncAuthType::AUTH_NONE ), _isMultipart(false ),
4344 _isPlainPost(false ), _expectingContinue(false ), _contentLength(0 ), _parsedLength(0 ), _multiParseState(0 ), _boundaryPosition(0 ), _itemStartIndex(0 ),
44- _itemSize(0 ), _itemName(), _itemFilename(), _itemType(), _itemValue(), _itemBuffer(0 ), _itemBufferIndex(0 ), _itemIsFile(false ),
45- _chunkedParseState(CHUNK_NONE ), _tempObject(NULL ) {
45+ _itemSize(0 ), _itemName(), _itemFilename(), _itemType(), _itemValue(), _itemBuffer(0 ), _itemBufferIndex(0 ), _itemIsFile(false ), _chunkStartIndex( 0 ),
46+ _chunkOffset( 0 ), _chunkSize( 0 ), _chunkedParseState(CHUNK_NONE ), _tempObject(NULL ) {
4647 c->onError (
4748 [](void *r, AsyncClient *c, int8_t error) {
4849 (void )c;
@@ -389,25 +390,45 @@ bool AsyncWebServerRequest::_parseChunkedBytes(uint8_t *buf, size_t len) {
389390 if (_chunkedParseState == CHUNK_LENGTH ) {
390391 // Incrementally decode a hex number
391392 if (data >= ' 0' && data <= ' 9' ) {
392- _chunkSize = (_chunkSize * 16 ) + (data - ' 0' );
393+ if (_chunkSize >= 0x1000000 ) {
394+ _chunkedParseState = CHUNK_ERROR ;
395+ } else {
396+ _chunkSize = (_chunkSize * 16 ) + (data - ' 0' );
397+ }
393398 } else if (data >= ' A' && data <= ' F' ) {
394- _chunkSize = (_chunkSize * 16 ) + (data - ' A' + 10 );
399+ if (_chunkSize >= 0x1000000 ) {
400+ _chunkedParseState = CHUNK_ERROR ;
401+ } else {
402+ _chunkSize = (_chunkSize * 16 ) + (data - ' A' + 10 );
403+ }
395404 } else if (data >= ' a' && data <= ' f' ) {
396- _chunkSize = (_chunkSize * 16 ) + (data - ' a' + 10 );
405+ if (_chunkSize >= 0x1000000 ) {
406+ _chunkedParseState = CHUNK_ERROR ;
407+ } else {
408+ _chunkSize = (_chunkSize * 16 ) + (data - ' a' + 10 );
409+ }
397410 } else if (data == ' ;' ) {
398411 _chunkedParseState = CHUNK_EXTENSION ;
412+ } else if (data == ' \r ' ) {
413+ // Ignore CR; wait for LF
399414 } else if (data == ' \n ' ) {
400415 _chunkOffset = 0 ;
401416 _chunkedParseState = CHUNK_DATA ;
417+ } else {
418+ // Invalid hex character
419+ _chunkedParseState = CHUNK_ERROR ;
402420 }
403421 } else if (_chunkedParseState == CHUNK_EXTENSION ) {
422+ // Chunk extensions appear after a semicolon.
423+ // We ignore them because their use cases are
424+ // specialized and obscure.
404425 if (data == ' \n ' ) {
405- // A zero length chunk marks the end of the chunk stream
406426 _chunkOffset = 0 ;
407427 _chunkedParseState = CHUNK_DATA ;
408428 }
409429 } else if (_chunkedParseState == CHUNK_END ) {
410430 if (data == ' \n ' ) {
431+ // A zero length chunk marks the end of the chunk stream
411432 if (_chunkSize == 0 ) {
412433 // If we needed to support trailers, we would switch to
413434 // TRAILER state, but since we have no use case for them,
@@ -417,6 +438,13 @@ bool AsyncWebServerRequest::_parseChunkedBytes(uint8_t *buf, size_t len) {
417438 _chunkSize = 0 ;
418439 _chunkedParseState = CHUNK_LENGTH ;
419440 }
441+ } else if (_chunkedParseState == CHUNK_ERROR ) {
442+ // If there was an error when parsing the chunk length, the
443+ // rest of the data stream is unreliable. Ideally we should
444+ // close the connection, but that risks leaving things dangling
445+ // (e.g. an open file), so it is probably best to just ignore
446+ // the rest of the data and give handleRequest a chance to
447+ // clean up.
420448 }
421449 }
422450 }
0 commit comments