From b6c6e41a0d94740f4ecce9afdafa0c17348ce4c0 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Sun, 30 May 2021 13:16:30 +0300 Subject: libnetfs, libtrivs: Shield S_file_exec_paths () from cancellation Here's the sequence of events that leads to a bug: * A task calls file_exec_paths () on itself, holding the only send right to the protid, passing EXEC_NEWTASK (or EXEC_SECURE, which implies it). * S_file_exec_paths () calls exec_exec_paths () * The exec server sets up a new task and calls proc_reassign () or proc_reauthenticate_reassign (). * The proc server destroys the old task, and the only send right to the protid with it. * The translator gets a no-senders notification, which results in a ports_interrupt_rpcs () call on the S_file_exec_paths (). * This propagates to the exec server (and potentially proc and auth servers). * The exec gets canceled, and the new task gets killed. In my opinion, the party that seems most guilty is the translator canceling exec upon receiving a no-senders notification for the protid. Normally, a no-senders notification means that the caller is no longer interested in the RPC, but this is not true in case of exec. To work around this, create an additional send right to the protid. --- libdiskfs/file-exec.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'libdiskfs') diff --git a/libdiskfs/file-exec.c b/libdiskfs/file-exec.c index 55b33a14..b1cb33c3 100644 --- a/libdiskfs/file-exec.c +++ b/libdiskfs/file-exec.c @@ -94,7 +94,7 @@ diskfs_S_file_exec_paths (struct protid *cred, mach_port_t execserver; int cached_exec; struct hurd_userlink ulink; - mach_port_t right; + mach_port_t right, cred_right; #define RETURN(code) do { err = (code); goto out; } while (0) @@ -195,6 +195,7 @@ diskfs_S_file_exec_paths (struct protid *cred, do { right = ports_get_send_right (newpi); + cred_right = ports_get_send_right (cred); #ifdef HAVE_EXEC_EXEC_PATHS err = exec_exec_paths (execserver, right, MACH_MSG_TYPE_COPY_SEND, @@ -221,6 +222,7 @@ diskfs_S_file_exec_paths (struct protid *cred, mach_port_deallocate (mach_task_self (), right); + mach_port_deallocate (mach_task_self (), cred_right); if (err == MACH_SEND_INVALID_DEST) { if (cached_exec) -- cgit v1.2.3