Skip to content

Commit 898b2f6

Browse files
committed
Check if port exists before calling erts_port_dec_refc in enif_port_command
Mimic check from `enif_is_port_alive` Fixes #11221
1 parent cd5a44f commit 898b2f6

3 files changed

Lines changed: 35 additions & 1 deletion

File tree

erts/emulator/beam/erl_nif.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1126,7 +1126,7 @@ enif_port_command(ErlNifEnv *env, const ErlNifPort* to_port,
11261126
else
11271127
res = erts_port_output_async(prt, c_p->common.id, msg);
11281128

1129-
if (scheduler <= 0)
1129+
if (scheduler <= 0 && prt)
11301130
erts_port_dec_refc(prt);
11311131

11321132
return res;

erts/emulator/test/dirty_nif_SUITE.erl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
dirty_process_register/1, dirty_process_trace/1,
4040
code_purge/1, literal_area/1, dirty_nif_send_traced/1,
4141
nif_whereis/1, nif_whereis_parallel/1, nif_whereis_proxy/1,
42+
dirty_port_command/1,
4243
set_halt_options_from_nif/1,
4344
delay_halt/1,
4445
delay_halt_old_code/1,
@@ -65,6 +66,7 @@
6566
dirty_heap_access_nif/1,
6667
whereis_term/2,
6768
whereis_send/3,
69+
dirty_port_command/2,
6870
dirty_terminating_literal_access/2,
6971
delay_halt_normal/3,
7072
delay_halt_io_bound/3,
@@ -94,6 +96,7 @@ all() ->
9496
dirty_nif_send_traced,
9597
nif_whereis,
9698
nif_whereis_parallel,
99+
dirty_port_command,
97100
{group, halt_normal},
98101
{group, halt_dirty_cpu},
99102
{group, halt_dirty_io},
@@ -1048,6 +1051,21 @@ nif_whereis(Config) when is_list(Config) ->
10481051
false = whereis_term(port, RegName),
10491052
ok.
10501053

1054+
%% enif_port_command() from a dirty scheduler must return false, not crash,
1055+
%% when the port is no longer alive (OTP-11221).
1056+
dirty_port_command(Config) when is_list(Config) ->
1057+
erl_ddll:try_load(?config(data_dir, Config), echo_drv, []),
1058+
1059+
Port = open_port({spawn, echo_drv}, [eof]),
1060+
Msg = ?MODULE_STRING " dirty port command\n",
1061+
ok = dirty_port_command(Port, Msg),
1062+
ok = receive {Port, {data, Msg}} -> ok after 1000 -> ct:fail(timeout) end,
1063+
1064+
%% Closed port: lookup returns NULL on the dirty path.
1065+
port_close(Port),
1066+
false = dirty_port_command(Port, Msg),
1067+
ok.
1068+
10511069
nif_whereis_parallel(Config) when is_list(Config) ->
10521070

10531071
%% try to be at least a little asymmetric
@@ -1179,6 +1197,7 @@ dirty_sleeper(_) -> ?nif_stub.
11791197
dirty_heap_access_nif(_) -> ?nif_stub.
11801198
whereis_term(_Type,_Name) -> ?nif_stub.
11811199
whereis_send(_Type,_Name,_Msg) -> ?nif_stub.
1200+
dirty_port_command(_Port,_Msg) -> ?nif_stub.
11821201
dirty_terminating_literal_access(_Me, _Literal) -> ?nif_stub.
11831202
delay_halt_normal(_Pid, _FileName, _Delay) -> ?nif_stub.
11841203
delay_halt_io_bound(_Pid, _FileName, _Delay) -> ?nif_stub.

erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,20 @@ whereis_send(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
545545
return whereis_result_term(env, rc);
546546
}
547547

548+
/* dirty_port_command(Port, Message) -> ok | false | badarg */
549+
static ERL_NIF_TERM
550+
dirty_port_command(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
551+
{
552+
ErlNifPort port;
553+
554+
assert(argc == 2);
555+
556+
if (!enif_get_local_port(env, argv[0], &port))
557+
return enif_make_badarg(env);
558+
559+
return enif_port_command(env, &port, NULL, argv[1]) ? atom_ok : atom_false;
560+
}
561+
548562
static ERL_NIF_TERM dirty_terminating_literal_access(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
549563
{
550564
ErlNifPid to, self;
@@ -750,6 +764,7 @@ static ErlNifFunc nif_funcs[] =
750764
{"dirty_call_while_terminated_nif", 1, dirty_call_while_terminated_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND},
751765
{"dirty_heap_access_nif", 1, dirty_heap_access_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND},
752766
{"whereis_send", 3, whereis_send, ERL_NIF_DIRTY_JOB_IO_BOUND},
767+
{"dirty_port_command", 2, dirty_port_command, ERL_NIF_DIRTY_JOB_IO_BOUND},
753768
{"whereis_term", 2, whereis_term, ERL_NIF_DIRTY_JOB_CPU_BOUND},
754769
{"dirty_terminating_literal_access", 2, dirty_terminating_literal_access, ERL_NIF_DIRTY_JOB_CPU_BOUND},
755770
{"delay_halt_normal", 3, delay_halt, 0},

0 commit comments

Comments
 (0)