Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-15-SP4:Update
nodejs16
CVE-2024-22019.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File CVE-2024-22019.patch of Package nodejs16
Index: node-v16.20.2/deps/llhttp/CMakeLists.txt =================================================================== --- node-v16.20.2.orig/deps/llhttp/CMakeLists.txt +++ node-v16.20.2/deps/llhttp/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.5.1) cmake_policy(SET CMP0069 NEW) -project(llhttp VERSION 6.0.11) +project(llhttp VERSION 6.1.0) include(GNUInstallDirs) set(CMAKE_C_STANDARD 99) Index: node-v16.20.2/deps/llhttp/include/llhttp.h =================================================================== --- node-v16.20.2.orig/deps/llhttp/include/llhttp.h +++ node-v16.20.2/deps/llhttp/include/llhttp.h @@ -2,8 +2,8 @@ #define INCLUDE_LLHTTP_H_ #define LLHTTP_VERSION_MAJOR 6 -#define LLHTTP_VERSION_MINOR 0 -#define LLHTTP_VERSION_PATCH 11 +#define LLHTTP_VERSION_MINOR 1 +#define LLHTTP_VERSION_PATCH 0 #ifndef LLHTTP_STRICT_MODE # define LLHTTP_STRICT_MODE 0 @@ -349,6 +349,9 @@ struct llhttp_settings_s { llhttp_cb on_headers_complete; /* Possible return values 0, -1, HPE_USER */ + llhttp_data_cb on_chunk_parameters; + + /* Possible return values 0, -1, HPE_USER */ llhttp_data_cb on_body; /* Possible return values 0, -1, `HPE_PAUSED` */ Index: node-v16.20.2/deps/llhttp/src/api.c =================================================================== --- node-v16.20.2.orig/deps/llhttp/src/api.c +++ node-v16.20.2/deps/llhttp/src/api.c @@ -355,6 +355,13 @@ int llhttp__on_chunk_header(llhttp_t* s, } +int llhttp__on_chunk_parameters(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_chunk_parameters, p, endp - p); + return err; +} + + int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) { int err; CALLBACK_MAYBE(s, on_chunk_complete); Index: node-v16.20.2/deps/llhttp/src/llhttp.c =================================================================== --- node-v16.20.2.orig/deps/llhttp/src/llhttp.c +++ node-v16.20.2/deps/llhttp/src/llhttp.c @@ -340,6 +340,8 @@ enum llparse_state_e { s_n_llhttp__internal__n_invoke_is_equal_content_length, s_n_llhttp__internal__n_chunk_size_almost_done, s_n_llhttp__internal__n_chunk_parameters, + s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters, + s_n_llhttp__internal__n_chunk_parameters_ows, s_n_llhttp__internal__n_chunk_size_otherwise, s_n_llhttp__internal__n_chunk_size, s_n_llhttp__internal__n_chunk_size_digit, @@ -539,6 +541,10 @@ int llhttp__on_body( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); +int llhttp__on_chunk_parameters( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + int llhttp__on_status( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); @@ -1226,8 +1232,7 @@ static llparse_state_t llhttp__internal_ goto s_n_llhttp__internal__n_chunk_parameters; } case 2: { - p++; - goto s_n_llhttp__internal__n_chunk_size_almost_done; + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_parameters; } default: { goto s_n_llhttp__internal__n_error_10; @@ -1236,6 +1241,34 @@ static llparse_state_t llhttp__internal_ /* UNREACHABLE */; abort(); } + case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters: + s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_chunk_parameters; + goto s_n_llhttp__internal__n_chunk_parameters; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_parameters_ows: + s_n_llhttp__internal__n_chunk_parameters_ows: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_parameters_ows; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_chunk_parameters_ows; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters; + } + } + /* UNREACHABLE */; + abort(); + } case s_n_llhttp__internal__n_chunk_size_otherwise: s_n_llhttp__internal__n_chunk_size_otherwise: { if (p == endp) { @@ -1246,13 +1279,9 @@ static llparse_state_t llhttp__internal_ p++; goto s_n_llhttp__internal__n_chunk_size_almost_done; } - case ' ': { - p++; - goto s_n_llhttp__internal__n_chunk_parameters; - } case ';': { p++; - goto s_n_llhttp__internal__n_chunk_parameters; + goto s_n_llhttp__internal__n_chunk_parameters_ows; } default: { goto s_n_llhttp__internal__n_error_11; @@ -6074,6 +6103,24 @@ static llparse_state_t llhttp__internal_ /* UNREACHABLE */; abort(); } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_parameters: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_parameters(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_chunk_size_almost_done; + /* UNREACHABLE */; + abort(); + } s_n_llhttp__internal__n_error_10: { state->error = 0x2; state->reason = "Invalid character in chunk parameters"; @@ -8441,6 +8488,8 @@ enum llparse_state_e { s_n_llhttp__internal__n_invoke_is_equal_content_length, s_n_llhttp__internal__n_chunk_size_almost_done, s_n_llhttp__internal__n_chunk_parameters, + s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters, + s_n_llhttp__internal__n_chunk_parameters_ows, s_n_llhttp__internal__n_chunk_size_otherwise, s_n_llhttp__internal__n_chunk_size, s_n_llhttp__internal__n_chunk_size_digit, @@ -8635,6 +8684,10 @@ int llhttp__on_body( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); +int llhttp__on_chunk_parameters( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + int llhttp__on_status( llhttp__internal_t* s, const unsigned char* p, const unsigned char* endp); @@ -9299,8 +9352,7 @@ static llparse_state_t llhttp__internal_ goto s_n_llhttp__internal__n_chunk_parameters; } case 2: { - p++; - goto s_n_llhttp__internal__n_chunk_size_almost_done; + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_parameters; } default: { goto s_n_llhttp__internal__n_error_6; @@ -9309,6 +9361,34 @@ static llparse_state_t llhttp__internal_ /* UNREACHABLE */; abort(); } + case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters: + s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_chunk_parameters; + goto s_n_llhttp__internal__n_chunk_parameters; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_parameters_ows: + s_n_llhttp__internal__n_chunk_parameters_ows: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_parameters_ows; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_chunk_parameters_ows; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters; + } + } + /* UNREACHABLE */; + abort(); + } case s_n_llhttp__internal__n_chunk_size_otherwise: s_n_llhttp__internal__n_chunk_size_otherwise: { if (p == endp) { @@ -9319,13 +9399,9 @@ static llparse_state_t llhttp__internal_ p++; goto s_n_llhttp__internal__n_chunk_size_almost_done; } - case ' ': { - p++; - goto s_n_llhttp__internal__n_chunk_parameters; - } case ';': { p++; - goto s_n_llhttp__internal__n_chunk_parameters; + goto s_n_llhttp__internal__n_chunk_parameters_ows; } default: { goto s_n_llhttp__internal__n_error_7; @@ -13951,6 +14027,24 @@ static llparse_state_t llhttp__internal_ /* UNREACHABLE */; abort(); } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_parameters: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_parameters(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_chunk_size_almost_done; + /* UNREACHABLE */; + abort(); + } s_n_llhttp__internal__n_error_6: { state->error = 0x2; state->reason = "Invalid character in chunk parameters"; Index: node-v16.20.2/doc/api/errors.md =================================================================== --- node-v16.20.2.orig/doc/api/errors.md +++ node-v16.20.2/doc/api/errors.md @@ -3043,6 +3043,18 @@ malconfigured clients, if more than 8 Ki HTTP parsing will abort without a request or response object being created, and an `Error` with this code will be emitted. +<a id="HPE_CHUNK_EXTENSIONS_OVERFLOW"></a> + +### `HPE_CHUNK_EXTENSIONS_OVERFLOW` + +<!-- YAML +added: REPLACEME +--> + +Too much data was received for a chunk extensions. In order to protect against +malicious or malconfigured clients, if more than 16 KiB of data is received +then an `Error` with this code will be emitted. + <a id="HPE_UNEXPECTED_CONTENT_LENGTH"></a> ### `HPE_UNEXPECTED_CONTENT_LENGTH` Index: node-v16.20.2/test/parallel/test-http-chunk-extensions-limit.js =================================================================== --- /dev/null +++ node-v16.20.2/test/parallel/test-http-chunk-extensions-limit.js @@ -0,0 +1,131 @@ +'use strict'; + +const common = require('../common'); +const http = require('http'); +const net = require('net'); +const assert = require('assert'); + +// Verify that chunk extensions are limited in size when sent all together. +{ + const server = http.createServer((req, res) => { + req.on('end', () => { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end('bye'); + }); + + req.resume(); + }); + + server.listen(0, () => { + const sock = net.connect(server.address().port); + let data = ''; + + sock.on('data', (chunk) => data += chunk.toString('utf-8')); + + sock.on('end', common.mustCall(function() { + assert.strictEqual(data, 'HTTP/1.1 413 Payload Too Large\r\nConnection: close\r\n\r\n'); + server.close(); + })); + + sock.end('' + + 'GET / HTTP/1.1\r\n' + + 'Host: localhost:8080\r\n' + + 'Transfer-Encoding: chunked\r\n\r\n' + + '2;' + 'A'.repeat(20000) + '=bar\r\nAA\r\n' + + '0\r\n\r\n' + ); + }); +} + +// Verify that chunk extensions are limited in size when sent in intervals. +{ + const server = http.createServer((req, res) => { + req.on('end', () => { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end('bye'); + }); + + req.resume(); + }); + + server.listen(0, () => { + const sock = net.connect(server.address().port); + let remaining = 20000; + let data = ''; + + const interval = setInterval( + () => { + if (remaining > 0) { + sock.write('A'.repeat(1000)); + } else { + sock.write('=bar\r\nAA\r\n0\r\n\r\n'); + clearInterval(interval); + } + + remaining -= 1000; + }, + common.platformTimeout(20), + ).unref(); + + sock.on('data', (chunk) => data += chunk.toString('utf-8')); + + sock.on('end', common.mustCall(function() { + assert.strictEqual(data, 'HTTP/1.1 413 Payload Too Large\r\nConnection: close\r\n\r\n'); + server.close(); + })); + + sock.write('' + + 'GET / HTTP/1.1\r\n' + + 'Host: localhost:8080\r\n' + + 'Transfer-Encoding: chunked\r\n\r\n' + + '2;' + ); + }); +} + +// Verify the chunk extensions is correctly reset after a chunk +{ + const server = http.createServer((req, res) => { + req.on('end', () => { + res.writeHead(200, { 'content-type': 'text/plain', 'connection': 'close', 'date': 'now' }); + res.end('bye'); + }); + + req.resume(); + }); + + server.listen(0, () => { + const sock = net.connect(server.address().port); + let data = ''; + + sock.on('data', (chunk) => data += chunk.toString('utf-8')); + + sock.on('end', common.mustCall(function() { + assert.strictEqual( + data, + 'HTTP/1.1 200 OK\r\n' + + 'content-type: text/plain\r\n' + + 'connection: close\r\n' + + 'date: now\r\n' + + 'Transfer-Encoding: chunked\r\n' + + '\r\n' + + '3\r\n' + + 'bye\r\n' + + '0\r\n' + + '\r\n', + ); + + server.close(); + })); + + sock.end('' + + 'GET / HTTP/1.1\r\n' + + 'Host: localhost:8080\r\n' + + 'Transfer-Encoding: chunked\r\n\r\n' + + '2;' + 'A'.repeat(10000) + '=bar\r\nAA\r\n' + + '2;' + 'A'.repeat(10000) + '=bar\r\nAA\r\n' + + '2;' + 'A'.repeat(10000) + '=bar\r\nAA\r\n' + + '0\r\n\r\n' + ); + }); +} Index: node-v16.20.2/tools/update-llhttp.sh =================================================================== --- node-v16.20.2.orig/tools/update-llhttp.sh +++ node-v16.20.2/tools/update-llhttp.sh @@ -59,5 +59,5 @@ echo "" echo "Please git add llhttp, commit the new version:" echo "" echo "$ git add -A deps/llhttp" -echo "$ git commit -m \"deps: update nghttp2 to $LLHTTP_VERSION\"" +echo "$ git commit -m \"deps: update llhttp to $LLHTTP_VERSION\"" echo "" Index: node-v16.20.2/lib/_http_server.js =================================================================== --- node-v16.20.2.orig/lib/_http_server.js +++ node-v16.20.2/lib/_http_server.js @@ -706,6 +706,11 @@ const requestHeaderFieldsTooLargeRespons `HTTP/1.1 431 ${STATUS_CODES[431]}\r\n` + 'Connection: close\r\n\r\n', 'ascii' ); +const requestChunkExtensionsTooLargeResponse = Buffer.from( + `HTTP/1.1 413 ${STATUS_CODES[413]}\r\n` + + 'Connection: close\r\n\r\n', 'ascii', +); + function socketOnError(e) { // Ignore further errors this.removeListener('error', socketOnError); @@ -719,6 +724,9 @@ function socketOnError(e) { case 'HPE_HEADER_OVERFLOW': response = requestHeaderFieldsTooLargeResponse; break; + case 'HPE_CHUNK_EXTENSIONS_OVERFLOW': + response = requestChunkExtensionsTooLargeResponse; + break; case 'ERR_HTTP_REQUEST_TIMEOUT': response = requestTimeoutResponse; break; Index: node-v16.20.2/src/node_http_parser.cc =================================================================== --- node-v16.20.2.orig/src/node_http_parser.cc +++ node-v16.20.2/src/node_http_parser.cc @@ -79,6 +79,8 @@ const uint32_t kOnExecute = 5; const uint32_t kOnTimeout = 6; // Any more fields than this will be flushed into JS const size_t kMaxHeaderFieldsCount = 32; +// Maximum size of chunk extensions +const size_t kMaxChunkExtensionsSize = 16384; const uint32_t kLenientNone = 0; const uint32_t kLenientHeaders = 1 << 0; @@ -206,6 +208,7 @@ class Parser : public AsyncWrap, public int on_message_begin() { num_fields_ = num_values_ = 0; + chunk_extensions_nread_ = 0; url_.Reset(); status_message_.Reset(); header_parsing_start_time_ = uv_hrtime(); @@ -443,9 +446,22 @@ class Parser : public AsyncWrap, public return 0; } - // Reset nread for the next chunk + int on_chunk_extension(const char* at, size_t length) { + chunk_extensions_nread_ += length; + + if (chunk_extensions_nread_ > kMaxChunkExtensionsSize) { + llhttp_set_error_reason(&parser_, + "HPE_CHUNK_EXTENSIONS_OVERFLOW:Chunk extensions overflow"); + return HPE_USER; + } + + return 0; + } + + // Reset nread for the next chunk and also reset the extensions counter int on_chunk_header() { header_nread_ = 0; + chunk_extensions_nread_ = 0; return 0; } @@ -887,6 +903,7 @@ class Parser : public AsyncWrap, public const char* current_buffer_data_; bool pending_pause_ = false; uint64_t header_nread_ = 0; + uint64_t chunk_extensions_nread_ = 0; uint64_t max_http_header_size_; uint64_t headers_timeout_; uint64_t header_parsing_start_time_ = 0; @@ -921,6 +938,7 @@ const llhttp_settings_t Parser::settings Proxy<DataCall, &Parser::on_header_field>::Raw, Proxy<DataCall, &Parser::on_header_value>::Raw, Proxy<Call, &Parser::on_headers_complete>::Raw, + Proxy<DataCall, &Parser::on_chunk_extension>::Raw, Proxy<DataCall, &Parser::on_body>::Raw, Proxy<Call, &Parser::on_message_complete>::Raw, Proxy<Call, &Parser::on_chunk_header>::Raw,
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor