Unmanaged calls are those that can come from unmanaged processes,
i.e. processes that the server does not control. They can also come from
managed processes, but they don't have to.
This commit does not introduce any unmanaged calls, however.
During local development, I created one and later decided to discard it.
However, this does seem like a useful feature, so it's being added with
this commit.
The main debugging code added is for keeping track of port names and
their associated IPC objects, as well as keeping track of the members of
port sets.
Additionally, when extended debugging is enabled, the server can now
wait for a debugger with the new env var `DSERVER_WAIT4DEBUGGER`.
The AsyncWriter class was originally written for some additional
debugging code I wrote but later decided wouldn't really be useful.
I kept the AsyncWriter class, however, as it seems it might be useful
for future code (it's basically fire-and-forget asynchronous writing).
Note that it has not been tested at all.
We now handle the sigexc calls as normal calls, with the exception that
it's okay for them to become active while another call was active.
We also set the thread's wait result to THREAD_INTERRUPTED and handle
syscall returns in interrupted continuations by jumping back to the
sigexc_enter code.
It doesn't support memory sharing or copying to a map other than the
current task yet. However, the LKM didn't support the latter case either,
so the only thing we're really missing is the ability to create a shared
region from a previously private one.
When a microthread went to sleep with a continuation, we discarded its
call. This would lead to the call being disposed before we had a chance
to reply to it. Instead, now we keep a reference to it in the thread
until we send a reply for it.
This function works properly now.
Additionally, add some more debug code to Mach port kqchannels,
but remove some debug code from `misc.c` (it causes issues with ASAN).
If we don't tell ASAN about what we're doing with microthreads, it gets
majorly confused about what's going on and generates lots of false
positives.
This is mainly because it poisons the stack, which we re-use when we
start a microthread from a new point (e.g. from a continuation or a new
call). The solution is simple: just tell ASAN that that memory for the
stack is OK when we do this.
Informing ASAN about our microthreads (which it calls "fibers") is just
a neat bonus to have it recognize our microthread stacks as actual stacks
and not just random memory.
This RPC call gives the caller a socket to which it can write to to log
to the server's log stream.
This is used to give userspace a place to put messages for "/dev/console".
Most notably, launchd tries to log to this device for important log
messages. This allows us to capture those messages.
So, when I discovered the mistake in `knote_post`, at first,
I was baffled at why we were even getting events if we were checking for
them and returning if there *were* events.
As it turns out, because the event checks for single ports and portsets
were switched, `dtape_kqchan_mach_port_has_events` was always returning `false`.
I fixed the issue there before I found the issue in `knote_post`,
so when I did that, suddenly, `knote_post` wasn't getting generating any
events. "WTF," I wondered. I spent *hours* trying to figure out why the
hell `knote_post` wouldn't return any events. Turns out that I was
missing an exclamation mark.
In the end, I decided to remove that check altogether, since the worst
that would happen is we have a spurious event and notify userspace and
userspace asks for an event when we don't have one, which we just tell them.
And with this commit, we can now reliably and consistently enter a shell
using darlingserver! 🎉
Thanks to the recently enabled assertions, it turns out that we actually need to disable IPC in tasks and threads before terminating it.
Additionally, let's lock threads when we should and not do so when we shouldn't. This should fix some thread synchronization issues I was running into.
XNU's kevent filter code returns FILTER_ACTIVE when there are events still available after a filter call. This tells kevent to check for more events at its discretion. Thus, to mimic this behavior, whenever we send a message, we check for more events afterwards.
Note that we only do this once the message has been fully sent. For reasons that are still unclear, checking immediately after queueing the message and queueing a new notification message as a result of this check messes up the order of the messages that our peer sees.
Instances of classes that inherit from the new Loggable class can now be logged directly to a Log::Stream. This makes it easier to log objects that provide context for a message.
Additionally, the kqchan code has been updated to use this new functionality to log kqchannels along with messages and make it easier to identify them and their processes.
Finally, for every message, the thread and process logging it is now recorded along with the message.
* Delete duct-tape/Makefile (it's a copy of the LKM's Makefile)
* Add a slash between the prefix directory and the socket filename so the socket is actually *in* the prefix and not next to it.
* Disable console logging by default and introduce a `DSERVER_LOG_STDERR` env var to optionally enable it
* EPIPE means the peer of the first message in a message queue died, so just drop the message; the main event loop will soon notice (via pidfd) that the peer died.
We use a kernel waiter thread to wait on the port set waitq. This is probably too much just for waiting on a port set; however, this is the best non-invasive solution. The other way to do this would be to modify the mqueue code to perform KNOTE on a port's port set (which is not easy to find; you'd have to walk through the port's waitq's waitq_sets looking for something that looks like a portset waitq_set).
Additionally, when a peer asks to read a Mach port kqchan and there are no events available, report it like we do for process kqchannels.
S2C calls were always failing because `_s2cPerform` was moving `_s2cReply` into a local variable (as it should) but then using `_s2cReply` (which is invalidated by the move) for error checking and returning that value instead of the moved local variable.
Also, copyinmap/copyout had the order of the arguments to memmove mixed up for the kernel_map case.
This commit also introduces the ability for thread execution to be deferred; this is used to prevent the thread from running while we're impersonating it.
Kernel threads can now be created and started in two separate actions (and it actually works now).
Also, this means we can remove the stupid hack we in dtape_thread_enter (that didn't even work); we should always clear TH_WAIT when the thread is going to run.