From 2ba7caaf76247acd3cb726776985b3dd209d972d Mon Sep 17 00:00:00 2001 From: Michael Santos Date: Sat, 27 Dec 2014 15:42:10 -0500 Subject: [PATCH] Exit with badpid for invalid OS PID in fork path A message sent to a non-existent Unix PID would be discarded, causing the calling erlang process to block forever in receive. This change modifies the behaviour of the port to return "badpid" if an element in the fork path is not found. Since any element of the path may be invalid, an event (as opposed to a call reply) is generated. Currently any "badpid" event will cause the erlang process to exit. Since only one call can be active at a time, it shouldn't interfere with other operations but the mailbox should pattern match on the list prefix (i.e., a call to [1,2,3,-1,4] would generate badpid for [1,2,3,-1]). Also define the behaviour when writing to invalid PIDs (<= 0). Since 0 is used to initialize the list of child PIDs in the port, passing in a PID of 0 would match the first available slot in the PID table, causing the port to write to fd 0. --- c_src/alcove.c | 9 ++++++++- src/alcove.app.src | 2 +- src/alcove_drv.erl | 6 ++++++ test/alcove_tests.erl | 24 ++++++++++++++++-------- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/c_src/alcove.c b/c_src/alcove.c index b062a6f..569ca08 100644 --- a/c_src/alcove.c +++ b/c_src/alcove.c @@ -377,7 +377,14 @@ alcove_stdin(alcove_state_t *ap) buf += 4; buflen -= 4; - (void)pid_foreach(ap, pid, buf, &buflen, pid_equal, write_to_pid); + if ( (pid <= 0) || (pid_foreach(ap, pid, buf, &buflen, pid_equal, + write_to_pid) == 1)) { + int tlen = 0; + char t[MAXMSGLEN] = {0}; + tlen = alcove_mk_atom(t, sizeof(t), "badpid"); + if (alcove_call_fake_reply(pid, ALCOVE_MSG_EVENT, t, tlen) < 0) + return -1; + } return 0; diff --git a/src/alcove.app.src b/src/alcove.app.src index 6ddfd45..d75a856 100644 --- a/src/alcove.app.src +++ b/src/alcove.app.src @@ -1,7 +1,7 @@ {application, alcove, [ {description, "Erlang application container/sandbox"}, - {vsn, "0.16.0"}, + {vsn, "0.17.0"}, {registered, []}, {applications, [ kernel, diff --git a/src/alcove_drv.erl b/src/alcove_drv.erl index acf9e62..a031979 100644 --- a/src/alcove_drv.erl +++ b/src/alcove_drv.erl @@ -185,6 +185,8 @@ call_reply(Drv, Pids, false, Timeout) -> receive {alcove_event, Drv, Pids, fdctl_closed} -> ok; + {alcove_event, Drv, _Pids, badpid} -> + exit(badpid); {alcove_call, Drv, Pids, Event} -> Event after @@ -193,6 +195,8 @@ call_reply(Drv, Pids, false, Timeout) -> end; call_reply(Drv, Pids, true, Timeout) -> receive + {alcove_event, Drv, _Pids, badpid} -> + exit(badpid); {alcove_event, Drv, Pids, {termsig,_} = Signal} -> exit(Signal); {alcove_event, Drv, Pids, fdctl_closed} -> @@ -211,6 +215,8 @@ call_reply(Drv, Pids, true, Timeout) -> reply(Drv, Pids, Type, Timeout) -> receive + {alcove_event, Drv, _Pids, badpid} -> + exit(badpid); {Type, Drv, Pids, Event} -> Event after diff --git a/test/alcove_tests.erl b/test/alcove_tests.erl index 02c482a..818d28b 100644 --- a/test/alcove_tests.erl +++ b/test/alcove_tests.erl @@ -410,18 +410,26 @@ badpid(#state{pid = Drv}) -> % EPIPE or PID not found ok = alcove:execvp(Drv, [Child], "/bin/sh", ["/bin/sh", "-c", "echo > /dev/null"]), - Reply0 = (catch alcove:call(Drv, [Child], execvp, ["/bin/sh", - ["/bin/sh", "-c", "echo > /dev/null"]], 1000)), - - PID = get_unused_pid(Drv), + waitpid_exit(Drv, [], Child), + Reply0 = (catch alcove:execvp(Drv, [Child], + "/bin/sh", ["/bin/sh", "-c", "echo > /dev/null"])), % PID not found - Reply1 = (catch alcove:call(Drv, [PID], execvp, ["/bin/sh", - ["/bin/sh", "-c", "echo > /dev/null"]], 1000)), + PID = get_unused_pid(Drv), + Reply1 = (catch alcove:execvp(Drv, [PID], + "/bin/sh", ["/bin/sh", "-c", "echo > /dev/null"])), + + % Invalid PIDs + Reply2 = (catch alcove:execvp(Drv, [-1], + "/bin/sh", ["/bin/sh", "-c", "echo > /dev/null"])), + Reply3 = (catch alcove:execvp(Drv, [0], + "/bin/sh", ["/bin/sh", "-c", "echo > /dev/null"])), [ - ?_assertEqual({'EXIT',timeout}, Reply0), - ?_assertEqual({'EXIT',timeout}, Reply1) + ?_assertEqual({'EXIT',badpid}, Reply0), + ?_assertEqual({'EXIT',badpid}, Reply1), + ?_assertEqual({'EXIT',badpid}, Reply2), + ?_assertEqual({'EXIT',badpid}, Reply3) ]. signal(#state{pid = Drv}) ->