Skip to content

Commit 420244e

Browse files
mcollinarichardlau
authored andcommitted
http: unset F_CHUNKED on new Transfer-Encoding
Duplicate `Transfer-Encoding` header should be a treated as a single, but with original header values concatenated with a comma separator. In the light of this, even if the past `Transfer-Encoding` ended with `chunked`, we should be not let the `F_CHUNKED` to leak into the next header, because mere presence of another header indicates that `chunked` is not the last transfer-encoding token. Ref: nodejs-private/llhttp-private#3 See: https://hackerone.com/bugs?report_id=1002188&subject=nodejs CVE-ID: CVE-2020-8287 PR-URL: nodejs-private/node-private#236 Reviewed-By: Fedor Indutny <[email protected]>
1 parent 4a30ac8 commit 420244e

File tree

2 files changed

+80
-2
lines changed

2 files changed

+80
-2
lines changed

deps/llhttp/src/llhttp.c

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,14 @@ int llhttp__internal__c_or_flags_16(
813813
return 0;
814814
}
815815

816+
int llhttp__internal__c_and_flags(
817+
llhttp__internal_t* state,
818+
const unsigned char* p,
819+
const unsigned char* endp) {
820+
state->flags &= -9;
821+
return 0;
822+
}
823+
816824
int llhttp__internal__c_update_header_state_7(
817825
llhttp__internal_t* state,
818826
const unsigned char* p,
@@ -5974,10 +5982,18 @@ static llparse_state_t llhttp__internal__run(
59745982
/* UNREACHABLE */;
59755983
abort();
59765984
}
5985+
s_n_llhttp__internal__n_invoke_and_flags: {
5986+
switch (llhttp__internal__c_and_flags(state, p, endp)) {
5987+
default:
5988+
goto s_n_llhttp__internal__n_header_value_te_chunked;
5989+
}
5990+
/* UNREACHABLE */;
5991+
abort();
5992+
}
59775993
s_n_llhttp__internal__n_invoke_or_flags_16: {
59785994
switch (llhttp__internal__c_or_flags_16(state, p, endp)) {
59795995
default:
5980-
goto s_n_llhttp__internal__n_header_value_te_chunked;
5996+
goto s_n_llhttp__internal__n_invoke_and_flags;
59815997
}
59825998
/* UNREACHABLE */;
59835999
abort();
@@ -7625,6 +7641,14 @@ int llhttp__internal__c_or_flags_16(
76257641
return 0;
76267642
}
76277643

7644+
int llhttp__internal__c_and_flags(
7645+
llhttp__internal_t* state,
7646+
const unsigned char* p,
7647+
const unsigned char* endp) {
7648+
state->flags &= -9;
7649+
return 0;
7650+
}
7651+
76287652
int llhttp__internal__c_update_header_state_7(
76297653
llhttp__internal_t* state,
76307654
const unsigned char* p,
@@ -12522,10 +12546,18 @@ static llparse_state_t llhttp__internal__run(
1252212546
/* UNREACHABLE */;
1252312547
abort();
1252412548
}
12549+
s_n_llhttp__internal__n_invoke_and_flags: {
12550+
switch (llhttp__internal__c_and_flags(state, p, endp)) {
12551+
default:
12552+
goto s_n_llhttp__internal__n_header_value_te_chunked;
12553+
}
12554+
/* UNREACHABLE */;
12555+
abort();
12556+
}
1252512557
s_n_llhttp__internal__n_invoke_or_flags_16: {
1252612558
switch (llhttp__internal__c_or_flags_16(state, p, endp)) {
1252712559
default:
12528-
goto s_n_llhttp__internal__n_header_value_te_chunked;
12560+
goto s_n_llhttp__internal__n_invoke_and_flags;
1252912561
}
1253012562
/* UNREACHABLE */;
1253112563
abort();
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
5+
const assert = require('assert');
6+
const http = require('http');
7+
const net = require('net');
8+
9+
const msg = [
10+
'POST / HTTP/1.1',
11+
'Host: 127.0.0.1',
12+
'Transfer-Encoding: chunked',
13+
'Transfer-Encoding: chunked-false',
14+
'Connection: upgrade',
15+
'',
16+
'1',
17+
'A',
18+
'0',
19+
'',
20+
'GET /flag HTTP/1.1',
21+
'Host: 127.0.0.1',
22+
'',
23+
'',
24+
].join('\r\n');
25+
26+
// Verify that the server is called only once even with a smuggled request.
27+
28+
const server = http.createServer(common.mustCall((req, res) => {
29+
res.end();
30+
}, 1));
31+
32+
function send(next) {
33+
const client = net.connect(server.address().port, 'localhost');
34+
client.setEncoding('utf8');
35+
client.on('error', common.mustNotCall());
36+
client.on('end', next);
37+
client.write(msg);
38+
client.resume();
39+
}
40+
41+
server.listen(0, common.mustCall((err) => {
42+
assert.ifError(err);
43+
send(common.mustCall(() => {
44+
server.close();
45+
}));
46+
}));

0 commit comments

Comments
 (0)