public
Last active

Reveals bug in prim_inet:close/1

  • Download Gist
close_bug.erl
Erlang
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
-module(close_bug).
 
-export([bad_ports/0, start/1]).
 
bad_ports() ->
[{P, element(2, erlang:port_info(P, connected))} || {P, {links, []}} <- [{P, erlang:port_info(P, links)} || P <- erlang:ports()]].
 
opts() ->
[binary, {active, false}, {exit_on_close, false}, {packet, 1}, {nodelay, true}].
 
start(N) ->
[] = bad_ports(),
erlang:process_flag(trap_exit, true),
{ok, Listen} = gen_tcp:listen(0, [{backlog, 100} | opts()]),
{ok, Port} = inet:port(Listen),
Pids = loop(N, Port, Listen, self()),
join(Pids),
ok = gen_tcp:close(Listen),
bad_ports().
 
loop(0, _, _, _) ->
[];
loop(N, Port, Listen, Self) ->
Pid = spawn_link(fun() -> accept(Port, Listen, Self) end),
[Pid | loop(N - 1, Port, Listen, Self)].
 
join([Pid | Next]) ->
receive
{'EXIT', Pid, _} ->
receive
{done, Pid} -> ok
after 0 ->
io:format("killed ~p\n", [Pid])
end
end,
join(Next);
join([]) ->
ok.
 
accept(Port, Listen, Parent) ->
spawn(fun() -> connect(Port) end),
{ok, S} = gen_tcp:accept(Listen, 5000),
{ok, <<"ping">>} = gen_tcp:recv(S, 0),
ok = gen_tcp:send(S, term_to_binary(self())),
receive
{close, Pid} -> ok
end,
true = erlang:link(Pid),
Pid ! {closing, self()},
ok = gen_tcp:close(S),
Parent ! {done, self()}.
 
connect(Port) ->
{ok, S} = gen_tcp:connect({0,0,0,0}, Port, opts()),
ok = gen_tcp:send(S, <<"ping">>),
{ok, B} = gen_tcp:recv(S, 0),
Pid = binary_to_term(B),
Pid ! {close, self()},
receive
{closing, Pid} -> exit(goodbye)
end.

As of R16B03-1 is seems to be fixed, cool!

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.