[erlang-patches] [PATCH] inet: error if fd does not match socket domain

Michael Santos <>
Tue May 24 14:02:25 CEST 2011

If an IPv4 fd is opened as an IPv6 socket, unexpected behaviour can
occur. For example, if an IPv4 UDP socket is opened and passed into
Erlang as an IPv6 socket, the first 3 bytes (corresponding to 1 byte
representing the protocol family, 2 bytes set to the port) are stripped
from the payload. The cause of the UDP payload truncation happens in
inet_drv.c:packet_inet_input when a call to inet_get_address fails
silently because the family is set to PF_INET6 but the buffer len is
the size of an IPv4 struct sockaddr_in.

Prevent this behaviour by checking that the protocol family of the file
descriptor matches the family of the requested Erlang socket.

    {ok, S1} = gen_udp:open(0, [binary, inet]),
    {ok, FD} = inet:getfd(S1),
    {ok, Port} = inet:port(S1),
    {ok, S} = gen_udp:open(Port, [binary, {fd, FD}, inet6]),
    {ok, C} = gen_udp:open(0, [binary]),
    Msg = <<1,2,3,4,5>>,
    gen_udp:send(C, "", Port, Msg),
        {udp, S, _, _, Msg} -> ok;
        {udp, S, _, _, NewMsg} -> {error, Msg, NewMsg}

This test results in: {error,<<1,2,3,4,5>>,<<4,5>>}

Thanks to Andrew Tunnell-Jones for finding the bug and the test case!
 erts/emulator/drivers/common/inet_drv.c |    4 +++-
 lib/kernel/test/gen_udp_SUITE.erl       |    1 +
 2 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 40c4a0d..ebc4469 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -3709,6 +3709,8 @@ static int inet_ctl_fdopen(inet_descriptor* desc, int domain, int type,
     /* check that it is a socket and that the socket is bound */
     if (IS_SOCKET_ERROR(sock_name(s, (struct sockaddr*) &name, &sz)))
 	return ctl_error(sock_errno(), rbuf, rsize);
+    if (name.sa.sa_family != domain)
+	return ctl_error(EINVAL, rbuf, rsize);
     desc->s = s;
     if ((desc->event = sock_create_event(desc)) == INVALID_EVENT)
 	return ctl_error(sock_errno(), rbuf, rsize);
@@ -9739,7 +9741,7 @@ static int packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len,
 	    if (desc->active || (len != 8))
 		return ctl_error(EINVAL, rbuf, rsize);
 	    timeout = get_int32(buf);
-	    /* The 2nd arg, Length(4), is ignored for both UDP ans SCTP protocols,
+	    /* The 2nd arg, Length(4), is ignored for both UDP and SCTP protocols,
 	       since they are msg-oriented. */
 	    if (enq_async(desc, tbuf, PACKET_REQ_RECV) < 0)
diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl
index d8a5519..b734d7f 100644
--- a/lib/kernel/test/gen_udp_SUITE.erl
+++ b/lib/kernel/test/gen_udp_SUITE.erl
@@ -400,6 +400,7 @@ open_fd(Config) when is_list(Config) ->
     {ok,S1}   = gen_udp:open(0),
     {ok,P2} = inet:port(S1),
     {ok,FD}   = prim_inet:getfd(S1),
+    {error,einval} = gen_udp:open(P2, [inet6, {fd,FD}]),
     {ok,S2}   = gen_udp:open(P2, [{fd,FD}]),
     {ok,S3}   = gen_udp:open(0),
     {ok,P3} = inet:port(S3),

More information about the erlang-patches mailing list