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

v8 crashes hard when setting property remoteAddress on wrapped TLS socket #8854

Closed
coolaj86 opened this issue Sep 29, 2016 · 6 comments
Closed
Labels
tls Issues and PRs related to the tls subsystem.

Comments

@coolaj86
Copy link
Contributor

coolaj86 commented Sep 29, 2016

  • Version: v6.6.0
  • Platform: Ubuntu 16.04.1 LTS Linux ubuntu-512mb-nyc1-01 4.4.0-31-generic path.resolve across drives in Windows #50-Ubuntu SMP Wed Jul 13 00:07:12 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
  • Subsystem: TLS and/or Stream

When assigning remoteAddress to a tlsSocket that is created from a Duplex, node crashes hard.

I discovered this as part of the workaround for #8752

tlsSocket.remoteAddress = 'x';
aj@ubuntu-512mb-nyc1-01:~/tunnel$ node crash.js
listening on 3001
r undefined
s ::ffff:127.0.0.1
t ::ffff:127.0.0.1
FATAL ERROR: v8::ToLocalChecked Empty MaybeLocal.
 1: node::Abort() [node]
 2: 0x8d04bc [node]
 3: v8::Utils::ReportApiFailure(char const*, char const*) [node]
 4: node::TLSWrap::OnStreamRead(long, uv_buf_t const&) [node]
 5: node::JSStream::ReadBuffer(v8::FunctionCallbackInfo<v8::Value> const&) [node]
 6: v8::internal::FunctionCallbackArguments::Call(void (*)(v8::FunctionCallbackInfo<v8::Value> const&)) [node]
 7: 0xb17d2c [node]
 8: v8::internal::Builtin_HandleApiCall(int, v8::internal::Object**, v8::internal::Isolate*) [node]
 9: 0x6670cd042fd
Aborted (core dumped)

crash.js:

(function () {
  'use strict';

  var net = require('net');
  var tls = require('tls');
  var hostname = 'local.test.ppl.family';
  var tlsOpts = {
    key: require('fs').readFileSync('./privkey.pem')
  , cert: require('fs').readFileSync('./fullchain.pem')
  };
  var Duplex = require('stream').Duplex;
  var WebSocket = require('ws');

  var tls3000 = tls.createServer(tlsOpts, function (tlsSocket) {
    console.log('r', tlsSocket.remoteAddress);
    console.log('s', tlsSocket._handle._parentWrap._handle.owner.stream.remoteAddress);
    tlsSocket._remoteAddress = tlsSocket._handle._parentWrap._handle.owner.stream.remoteAddress;
    console.log('t', tlsSocket._remoteAddress);
    tlsSocket.remoteAddress = 'x'; // causes core dump
    console.log('u', tlsSocket.remoteAddress);
  });

  var Dup = {
    write: function (chunk, encoding, cb) {
      //console.log('_write', chunk.byteLength);
      this.__my_socket.write(chunk, encoding);
      cb();
    }
  , read: function (size) {
      //console.log('_read');
      var x = this.__my_socket.read(size);
      if (x) {
        console.log('_read', size);
        this.push(x);
      }
    }
  };

  function connectHttps(servername, socket) {
    var myDuplex = new Duplex();

    myDuplex.__my_socket = socket;
    myDuplex._write = Dup.write;
    myDuplex._read = Dup.read;
    myDuplex.remoteFamily = socket.remoteFamily;
    myDuplex.remoteAddress = socket.remoteAddress;
    myDuplex.remotePort = socket.remotePort;
    myDuplex.localFamily = socket.localFamily;
    myDuplex.localAddress = socket.localAddress;
    myDuplex.localPort = socket.localPort;

    tls3000.emit('connection', myDuplex);

    socket.on('data', function (chunk) {
      myDuplex.push(chunk);
    });
  }

  var tcp3000 = net.createServer();
  tcp3000.listen(3001, function () {
    console.log('listening on 3001');

    var wstunneler = new WebSocket('wss://' + hostname + ':3001/?access_token=xyz', { rejectUnauthorized: false });
    wstunneler.on('open', function () {
      console.log('ws open');
    });
  });

  tcp3000.on('connection', function (socket) {
    socket.once('data', function (firstChunk) {
      connectHttps(null, socket);
      socket.unshift(firstChunk);
    });

  });
}());

It may be possible to slim this down a little further and still get it to crash if #8752 gets fixed... except that the remoteAddress property would probably show up as expected and then I wouldn't be trying to write to it in the first place...

@mscdex mscdex added the tls Issues and PRs related to the tls subsystem. label Sep 29, 2016
@mscdex
Copy link
Contributor

mscdex commented Sep 29, 2016

/cc @indutny

@coolaj86
Copy link
Contributor Author

coolaj86 commented Apr 27, 2017

I also get the same FATAL ERROR: v8::ToLocalChecked Empty MaybeLocal. error when doing this (same situation as above, but without setting the remateAddress variable either):

tls.createServer(tunnelAdminTlsOpts, function (tlsSocket) {
    ( thing.notDefined || thing.notDefinedEither ).oopsThisWontBeDefined(tlsSocket);
});

Which seems very much like it should throw an error in JS, not in V8.

Sidenote:

For reference, this is where the remote address is, if anyone needs it:

tlsSocket._handle._parent.owner.stream.remoteAddress

@Trott
Copy link
Member

Trott commented Aug 6, 2017

@nodejs/crypto

@coolaj86
Copy link
Contributor Author

coolaj86 commented May 4, 2018

I've tested in node v9.11.1 and this is still an issue.

If you'd like to get some ssl certs to test with you can click-click-next through the process here: https://greenlock.ppl.family/

@Trott
Copy link
Member

Trott commented May 5, 2018

I've tested in node v9.11.1 and this is still an issue.

@coolaj86 Would it be easy enough to test with 10.0.0? (9.x goes EOL in a couple months, but 10.x will be supported for another 2.5 years.)

@coolaj86
Copy link
Contributor Author

Yep, still a problem in 10.0.0

r undefined
s ::ffff:127.0.0.1
t ::ffff:127.0.0.1
node[4922]: ../src/node.cc:1379:void node::ReportException(node::Environment*, v8::Local<v8::Value>, v8::Local<v8::Message>): Assertion `!message.IsEmpty()' failed.
 1: node::Abort() [node]
 2: 0x87b6c5 [node]
 3: 0x87c41a [node]
 4: node::FatalException(v8::Isolate*, v8::Local<v8::Value>, v8::Local<v8::Message>) [node]
 5: node::FatalException(v8::Isolate*, v8::TryCatch const&) [node]
 6: node::JSStream::DoWrite(node::WriteWrap*, uv_buf_t*, unsigned long, uv_stream_s*) [node]
 7: node::TLSWrap::EncOut() [node]
 8: node::TLSWrap::OnStreamRead(long, uv_buf_t const&) [node]
 9: node::JSStream::ReadBuffer(v8::FunctionCallbackInfo<v8::Value> const&) [node]
10: v8::internal::FunctionCallbackArguments::Call(v8::internal::CallHandlerInfo*) [node]
11: 0xad62fa [node]
12: v8::internal::Builtin_HandleApiCall(int, v8::internal::Object**, v8::internal::Isolate*) [node]
13: 0x3e9fce0427d
Aborted (core dumped)

bnoordhuis added a commit to bnoordhuis/io.js that referenced this issue May 14, 2018
This bug needs a test case with a high goldilocks factor to trigger
but the synopsis is that `v8::TryCatch::Message()` returns an empty
handle when the TryCatch is declared at a time when an exception is
already pending.

We now recompute the message inside `node::ReportException()` and
all is well again.

Fixes: nodejs#8854
MylesBorins pushed a commit that referenced this issue May 22, 2018
This bug needs a test case with a high goldilocks factor to trigger
but the synopsis is that `v8::TryCatch::Message()` returns an empty
handle when the TryCatch is declared at a time when an exception is
already pending.

We now recompute the message inside `node::ReportException()` and
all is well again.

PR-URL: #20708
Fixes: #8854
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Anatoli Papirovski <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Daniel Bevenius <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tls Issues and PRs related to the tls subsystem.
Projects
None yet
Development

No branches or pull requests

3 participants