Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: do not assume tls handshake order #25508

Closed
wants to merge 11 commits into from

Conversation

sam-github
Copy link
Contributor

@sam-github sam-github commented Jan 15, 2019

These are a number of cleanups to the TLS test suites that I noticed while trying to run the tests with TLS1.3, but they aren't specific to TLS1.3. I'd like them off my branch so its clear they are equally applicable to TLS1.2, that is, they are not API breaking changes being introduced with TLS1.3.

Mostly they involve removing assumptions about the relative ordering of client and server TLS handshake completion, graceful use of .end() to tear down tests (rather than destroy), and some conversions of tests to use common.mustCall()

PTAL @nodejs/crypto

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines

@nodejs-github-bot
Copy link
Collaborator

@sam-github sadly an error occured when I tried to trigger a build :(

@nodejs-github-bot nodejs-github-bot added the test Issues and PRs related to the tests. label Jan 15, 2019
@sam-github
Copy link
Contributor Author

@sam-github sam-github force-pushed the cleanup-tls-test-shutdown branch from 7960c8d to 5520871 Compare January 15, 2019 04:29
@sam-github
Copy link
Contributor Author

Sorry, best assume this is a WIP, I found another example of the same problem. I'll add any more I find rather than fix the same thing in multiple commits.

@sam-github sam-github force-pushed the cleanup-tls-test-shutdown branch from 5520871 to 80f95c7 Compare January 15, 2019 04:34
@mscdex mscdex added the wip Issues and PRs that are still a work in progress. label Jan 15, 2019
@sam-github sam-github force-pushed the cleanup-tls-test-shutdown branch 2 times, most recently from fb375cb to 7945a77 Compare February 1, 2019 21:09
@sam-github sam-github added tls Issues and PRs related to the tls subsystem. and removed wip Issues and PRs that are still a work in progress. labels Feb 1, 2019
@sam-github sam-github force-pushed the cleanup-tls-test-shutdown branch from 7945a77 to b3cb13b Compare February 1, 2019 21:19
@sam-github
Copy link
Contributor Author

ci: https://ci.nodejs.org/job/node-test-pull-request/20525/

@nodejs/crypto, I reworked the description, it was out of date.

@sam-github
Copy link
Contributor Author

@nodejs/collaborators please take a look

@sam-github
Copy link
Contributor Author

@indutny perhaps you could take a look at this one?

test/parallel/test-tls-socket-close.js Outdated Show resolved Hide resolved
client.write('x');
client.on('data', (data) => {
socket.end(BAD_RECORD);
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the only change that I don’t understand – why is .write('x') necessary? Or is it not necessary and does it just expand the test?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the delay of the BAD_RECORD until .on(data) that is necessary. What's happening on the server side at the point that the client handshake completes depends on the protocol version, so the behaviour with a bad record varies, and the relative ordering of the client secureConnect and server secureConnection are undefined. IIRC, on 1.3 the bad record interrupts the server handshake, causing a tlsClientError (thus the common.mustNotCall() I introduced for that event) instead of a secureConnection event. So, I introduced a data exchange. The server is doing client.pipe(client) to echo data back, so I write from the client, and when the data echoes back, I know that the handshake must have completed on the server.

Test assumed server gets a handshake before the client destroyed it, and
didn't assert that dns.lookup() callback occurred.
Test assumed that server got the the connection before the client
destroys it, but that is not guaranteed. Also, the test was closing the
TCP connection 3 times, effectively:

1. on the server side, right after TLS connection occurs (if it does)
2. on the client side, internal to tls, when the cert is rejected
3. again on the client side, in the error event which is emitted by
   the internal tls destroy from 2

This is too often, and the dependency on 1 occurring is fragile.
Do not assume that server handshake event happens before client, it is
not guaranteed.
Existing code assumed that the server completed the handshake before the
client rejected the certificate, and destroyed the socket. This
assumption is fragile, remove it, and instead check explicitly that data
can or cannot be exchanged via TLS, whichever is expected.
This test has a dependency on the order in which the TCP connection is
made, and TLS server handshake completes. It assumes those server side
events occur before the client side write callback, which is not
guaranteed by the TLS API. It usually passes with TLS1.3, but TLS1.3
didn't exist at the time the bug existed.

Pin the test to TLS1.2, since the test shouldn't be changed in a way that
doesn't trigger a segfault in 7.7.3:
- nodejs#13184 (comment)
The timing of destroy between client/server is undefined, but end is
guaranteed to occur.
Connection is known to be completely setup only after data has
exchanged, so wait unil data echo before sending a bad record.
Otherwise, the bad record could interrupt completion of the server's
handshake, and whether the error is emitted on the connection or server
is a matter of timing.

Also, assert that server errors do not occur. 'error' would crash node
with and unhandled event, but 'tlsClientError' is ignored by default.
Instead of pushing state into global arrays and checking the results
before exit, use common.mustCall() and make the checks immediately.
Fix perplexing comment. It's not that TLS "clients" don't support
'secureConnect', it's that client sockets created with `new TLSSocket`
(as opposed to `tls.connect()`) don't support that event.
@sam-github sam-github deleted the cleanup-tls-test-shutdown branch February 6, 2019 23:20
sam-github added a commit that referenced this pull request Feb 6, 2019
Test assumed server gets a handshake before the client destroyed it, and
didn't assert that dns.lookup() callback occurred.

PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
sam-github added a commit that referenced this pull request Feb 6, 2019
Test assumed that server got the the connection before the client
destroys it, but that is not guaranteed. Also, the test was closing the
TCP connection 3 times, effectively:

1. on the server side, right after TLS connection occurs (if it does)
2. on the client side, internal to tls, when the cert is rejected
3. again on the client side, in the error event which is emitted by
   the internal tls destroy from 2

This is too often, and the dependency on 1 occurring is fragile.

PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
sam-github added a commit that referenced this pull request Feb 6, 2019
Do not assume that server handshake event happens before client, it is
not guaranteed.

PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
sam-github added a commit that referenced this pull request Feb 6, 2019
Existing code assumed that the server completed the handshake before the
client rejected the certificate, and destroyed the socket. This
assumption is fragile, remove it, and instead check explicitly that data
can or cannot be exchanged via TLS, whichever is expected.

PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
sam-github added a commit that referenced this pull request Feb 6, 2019
This test has a dependency on the order in which the TCP connection is
made, and TLS server handshake completes. It assumes those server side
events occur before the client side write callback, which is not
guaranteed by the TLS API. It usually passes with TLS1.3, but TLS1.3
didn't exist at the time the bug existed.

Pin the test to TLS1.2, since the test shouldn't be changed in a way that
doesn't trigger a segfault in 7.7.3:
- #13184 (comment)

PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
sam-github added a commit that referenced this pull request Feb 6, 2019
The timing of destroy between client/server is undefined, but end is
guaranteed to occur.

PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
sam-github added a commit that referenced this pull request Feb 6, 2019
Connection is known to be completely setup only after data has
exchanged, so wait unil data echo before sending a bad record.
Otherwise, the bad record could interrupt completion of the server's
handshake, and whether the error is emitted on the connection or server
is a matter of timing.

Also, assert that server errors do not occur. 'error' would crash node
with and unhandled event, but 'tlsClientError' is ignored by default.

PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
sam-github added a commit that referenced this pull request Feb 6, 2019
PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
sam-github added a commit that referenced this pull request Feb 6, 2019
PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
sam-github added a commit that referenced this pull request Feb 6, 2019
Instead of pushing state into global arrays and checking the results
before exit, use common.mustCall() and make the checks immediately.

PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
sam-github added a commit that referenced this pull request Feb 6, 2019
Fix perplexing comment. It's not that TLS "clients" don't support
'secureConnect', it's that client sockets created with `new TLSSocket`
(as opposed to `tls.connect()`) don't support that event.

PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
addaleax pushed a commit that referenced this pull request Feb 7, 2019
Test assumed server gets a handshake before the client destroyed it, and
didn't assert that dns.lookup() callback occurred.

PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
addaleax pushed a commit that referenced this pull request Feb 7, 2019
Test assumed that server got the the connection before the client
destroys it, but that is not guaranteed. Also, the test was closing the
TCP connection 3 times, effectively:

1. on the server side, right after TLS connection occurs (if it does)
2. on the client side, internal to tls, when the cert is rejected
3. again on the client side, in the error event which is emitted by
   the internal tls destroy from 2

This is too often, and the dependency on 1 occurring is fragile.

PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
addaleax pushed a commit that referenced this pull request Feb 7, 2019
Do not assume that server handshake event happens before client, it is
not guaranteed.

PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
addaleax pushed a commit that referenced this pull request Feb 7, 2019
Existing code assumed that the server completed the handshake before the
client rejected the certificate, and destroyed the socket. This
assumption is fragile, remove it, and instead check explicitly that data
can or cannot be exchanged via TLS, whichever is expected.

PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
addaleax pushed a commit that referenced this pull request Feb 7, 2019
Connection is known to be completely setup only after data has
exchanged, so wait unil data echo before sending a bad record.
Otherwise, the bad record could interrupt completion of the server's
handshake, and whether the error is emitted on the connection or server
is a matter of timing.

Also, assert that server errors do not occur. 'error' would crash node
with and unhandled event, but 'tlsClientError' is ignored by default.

PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
addaleax pushed a commit that referenced this pull request Feb 7, 2019
PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
addaleax pushed a commit that referenced this pull request Feb 7, 2019
PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
addaleax pushed a commit that referenced this pull request Feb 7, 2019
Instead of pushing state into global arrays and checking the results
before exit, use common.mustCall() and make the checks immediately.

PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
addaleax pushed a commit that referenced this pull request Feb 7, 2019
Fix perplexing comment. It's not that TLS "clients" don't support
'secureConnect', it's that client sockets created with `new TLSSocket`
(as opposed to `tls.connect()`) don't support that event.

PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
addaleax pushed a commit that referenced this pull request Feb 8, 2019
This test has a dependency on the order in which the TCP connection is
made, and TLS server handshake completes. It assumes those server side
events occur before the client side write callback, which is not
guaranteed by the TLS API. It usually passes with TLS1.3, but TLS1.3
didn't exist at the time the bug existed.

Pin the test to TLS1.2, since the test shouldn't be changed in a way that
doesn't trigger a segfault in 7.7.3:
- #13184 (comment)

PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
addaleax pushed a commit that referenced this pull request Feb 8, 2019
The timing of destroy between client/server is undefined, but end is
guaranteed to occur.

PR-URL: #25508
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
@targos targos mentioned this pull request Feb 14, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
test Issues and PRs related to the tests. tls Issues and PRs related to the tls subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants