Skip to content

Crash in AddressToJS for udp6 socket bound to link-local address #41500

Closed
@darkk

Description

@darkk

Version

v17.3.1

Platform

Linux darkk-ya-laptop 4.4.0-210-generic #242-Ubuntu SMP Fri Apr 16 09:57:56 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

Subsystem

dgram

What steps will reproduce the bug?

Use the following four files to build the test-case:

  • dgram-udp6-address.js:
'use strict';

const dgram = require('dgram');
const socket = dgram.createSocket({ type: 'udp6' });

const discard_port = 9;
const global_ipv6 = '2600::';

// It might be rejected if there is no route to the ip (e.g. IPv4-only network).
socket.connect(discard_port, global_ipv6, err => {
  if (err === undefined) {
    console.log('remoteAddress: ', socket.remoteAddress());
    // The next line triggers assertion under certain conditions:
    // node[631]: ../src/tcp_wrap.cc:354:v8::Local<v8::Object> node::AddressToJS(node::Environment*, const sockaddr*, v8::Local<v8::Object>): Assertion `(r) == (0)' failed.
    console.log('address: ', socket.address());
  } else {
    console.log('UDP connection error', err);
  }
  socket.unref();
});
  • Dockerfile:
FROM node:17-bullseye

RUN set -ex \
    && apt-get update \
    && apt-get install -y --no-install-recommends iproute2 \
    && :

COPY --chown=0:0 \
    dgram-udp6-address.js \
    test.sh \
    /
  • test.sh:
#!/bin/bash

set -ex

ip link add dummy0 type dummy
ip link set dummy0 up
ip -6 address add fe80::aaaa:bbbb:cccc:dddd dev dummy0
ip -6 route add default dev dummy0
ip addr
ip -6 route
node --version
node /dgram-udp6-address.js
  • run.sh:
#!/bin/bash

set -ex

tar cz Dockerfile dgram-udp6-address.js test.sh | docker build -t dgram-udp6-address:latest -

exec docker run --rm --tty -i \
    --privileged \
    --net=none \
     --sysctl net.ipv6.conf.all.disable_ipv6=0 \
    dgram-udp6-address:latest \
    /bin/bash /test.sh

The test-case is run with ./run.sh.

How often does it reproduce? Is there a required condition?

It's 100% reproducible with this network set-up.

This network setup simulates the default setup of Rostelecom ISP in St. Petersburg regarding their handling of IPv6 as seen by my Ubuntu 16.04 laptop:

  • the ISP-provided router announces a default gateway via link-local address fe80::1
  • local machines get link-local address
  • no real IPv6 connectivity is provided
  • the router responds with ICMP6, destination unreachable, unreachable route to IPv6 packets with global destination

What is the expected behavior?

node should not crash whole app. It should rather be some kind of exception, socket error or something like that.

I've noticed the behavior while developing Electron app, whole app was terminated on socket.address() call.

What do you see instead?

...
+ node /dgram-udp6-address.js
remoteAddress:  { address: '2600::', family: 'IPv6', port: 9 }
node[15]: ../src/tcp_wrap.cc:372:v8::Local<v8::Object> node::AddressToJS(node::Environment*, const sockaddr*, v8::Local<v8::Object>): Assertion `(r) == (0)' failed.
 1: 0xb2c330 node::Abort() [node]
 2: 0xb2c3ae  [node]
 3: 0xc1da97 node::AddressToJS(node::Environment*, sockaddr const*, v8::Local<v8::Object>) [node]
 4: 0xc2f2ad void node::GetSockOrPeerName<node::UDPWrap, &uv_udp_getsockname>(v8::FunctionCallbackInfo<v8::Value> const&) [node]
 5: 0xd798de  [node]
 6: 0xd7acff v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) [node]
 7: 0x162fff9  [node]
/test.sh: line 12:    15 Aborted                 (core dumped) node /dgram-udp6-address.js

Additional information

Other affected versions are v12.22.8, v14.18.2, v16.13.1

Relevant strace snippet is:

socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 17
bind(17, {sa_family=AF_INET6, sin6_port=htons(0), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::", &sin6_addr), sin6_scope_id=0}, 28) = 0
connect(17, {sa_family=AF_INET6, sin6_port=htons(9), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "2600::", &sin6_addr), sin6_scope_id=0}, 28) = 0
getpeername(17, {sa_family=AF_INET6, sin6_port=htons(9), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "2600::", &sin6_addr), sin6_scope_id=0}, [128->28]) = 0

remoteAddress:  { address: '2600::', family: 'IPv6', port: 9 }

getsockname(17, {sa_family=AF_INET6, sin6_port=htons(44890), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "fe80::aaaa:bbbb:cccc:dddd", &sin6_addr), sin6_scope_id=0}, [128->28]) = 0
access("/proc/net", R_OK)               = 0
access("/proc/net/unix", R_OK)          = 0
socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 20
ioctl(20, SIOCGIFNAME, {ifr_index=0})   = -1 ENODEV (No such device)
close(20)                               = 0
getpid()                                = 18

node[18]: ../src/tcp_wrap.cc:372:v8::Local<v8::Object> node::AddressToJS(node::Environment*, const sockaddr*, v8::Local<v8::Object>): Assertion `(r) == (0)' failed.

So, the kernel returns sin6_flowinfo=htonl(0) for the bound socket, so the assertion is false.

#39143 seems to be a similar issue on AIX.

Metadata

Metadata

Assignees

No one assigned

    Labels

    dgramIssues and PRs related to the dgram subsystem / UDP.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions