New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SSCSM execution #13046
SSCSM execution #13046
Conversation
Is this a draft for the SSCSM? I have heard many good things about SSCSM. What are some examples of the things that become possible or easier with it? |
This is just a proof of concept for SSCSM (yes, the SSCSM) execution. There's a lot more work to do. This proof of concept may even be discarded. Once implemented, SSCSMs will let mods do client-side entity movement prediction, for example. |
👍 Good work Worth noting that there's no reason to only sandbox SSCSM and not the "normal" CSM too.
Can't you fix this by adding timeouts to IPC calls? That'd be much cleaner. How is the "sscsm asks a question back to the client" direction handled, are these processed synchronously while waiting for the current callback to end or does that run in a separate thread? The latter is bound to cause problems and doesn't fit our model. Lastly, for IPC I think it'd save us a lot of work if we didn't reinvent the wheel but use an existing library that hopefully can make exchanging classes and entire structures seamless. |
A timeout could probably be set with Here is how callbacks are called:
I haven't used IPC libraries before, so I'm up for suggestions. Ideally there shouldn't be a limit on message length. |
I think the implementation of regular CSMs should be kept separate, as they have a different API. For example, it doesn't make sense to give SSCSMs access to the same mod storage. Best to avoid rewriting the regular CSM API, IMO. |
Okay that sounds good.
I don't have any. Worth looking into.
CSM isn't the goal, it's a byproduct of SSCSM. CSM has to serve the needs of games/mods to offload suitable tasks to the client (including some which only make sense on the client), and then the parts of that that can be safely exposed to client mods will be. |
Right now pipe inputs are wrapped in a |
I guess I can use |
How about having two processes, one for SSCSM and one for client-provided CSM. That would allow for sharing most of the code. |
CPCSM is only as risky as server side modding. It's not worth putting effort in sandboxing that too. The only relevant performance issues that we get is the latency of the IPC calls, right? Some suggestions here:
Btw. I've once tried different IPC mechanisms to get IPC calls done with as low latency as possible. The best I was able to come up with was using futexes and doing busy waiting (I've just adjusted the example from the futex man page), resulting in an average latency of about You might want to put the sscsm process code into a separate, smaller executable, so there's fewer (dead) code, globals and other stuff that an attacker could use. Do we want an option to opt-out of the process sandbox, i.e. for more performance in singleplayer? ((Another use-case: Maybe we will move some code from C++ to SSCSM client builtin (SSCSMCB) for dehardcoding (i.e. player physics, or many things from |
I've thought about using shared memory for communication. However, the SSCSM process is not trusted. One advantage of a pipe is that the process on one end doesn't have to trust the process on the other. With shared memory, this would have to verified by looking at the communication implementation. As far as I know, this concern is not addressed by most synchronization libraries, including pthreads, so Minetest might have to provide its own implementation for each platform based on the raw system calls. This would be easy to get wrong. |
By the way, when you were benchmarking IPC, did you benchmark pipes? |
No. (I've read somewhere that they had hight latency (well, obviously, if you block on every wait), and therefore didn't try.) |
Here's a benchmark that tests both pipes and shared memory: https://stackoverflow.com/questions/1235958/ipc-performance-named-pipe-vs-socket. I don't know the exact setup, but shared memory is indeed much faster. |
Sure, whether one or two processes does not matter much. The important point is that SSCSM and CPCSM share the parts of the code they can.
Too complex, you immediately run into problems because you still need to serialize the calls that have interdependent side-effects (what if someone calls
While you could argue that this is defense-in-depth this is pretty much irrelevant if we sandbox the process in any reasonable way.
For platforms like Android we need to think of an alternative anyway since you can't launch processes there (e.g. running in a thread, unsandboxed) and we'll probably have a development option for that, but this must never be marketed to users as a performance option.
FYI there are mechanisms to do this safely on Linux even with an untrusted client. Wayland uses them, some material: https://lwn.net/Articles/591108/ |
Is it actually impossible to create a new process in Android? It seems like |
Android apps can have multiple processes (in Java at least). Not sure how it works |
My thought was that the main process always first completes all asynchronous messages before doing a synchronous one. set_node(pos, node1) -- lua calls to c++, c++ enqueues the `set_node` msg (and signals main thread) and directly returns
set_node(pos, node2) -- same as above
get_node(pos) -- lua calls to c++, c++ writes the `get_node` into shared mem, signals main thread, and waits for all pending calls to finish, then it returns I guess I should've written "implement the API asynchronously", as the API still looks and works as if it was synchronous.
It looks to me like sealing is useful if one wants to transfer large amounts of data with zero-copy. But in our case, copying the data is much cheaper than doing syscalls to create a file, seal it, send it, and close it. |
You'd only do that once and then keep the shared buffer. I looked for suitable libraries a bit. IPC:
That's just for pushing bytes to another process. For serialization I had hoped there was a library that can use template magic to make serializing structs/classes painless but this doesn't seem to exist (I didn't look very hard). Since this is quite complex affair IMO we should be thinking about the kinds of objects we want to transfer/access from the other process and how that could work:
Most of it will have to be transferred on demand anew each step, maybe we can think of some caching mechanisms. |
Oh yeah, I hadn't thought about the lack of a native executable on Android. I suppose C++ could call to Java which could launch the process. That can be tackled later. For objects, I imagine a solution similar to file descriptors: the SSCSM process keeps track of objects with numeric handles and the main process keeps a mapping from these handles to the objects. VoxelManips, on the other hand, could probably be kept entirely within the SSCSM process. |
I think that file sealing thing is not necessary for us. The SSCSM process can just map the file into memory then close the file descriptor before relinquishing its privileges. |
Apparently ZeroMQ uses Unix domain sockets for IPC: http://api.zeromq.org/4-3:zmq-ipc. Therefore, it probably won't be faster than regular pipes. It might be best to use a custom ring buffer implementation. I think the pthreads API will suffice. There may be some risk of a malicious program breaking stuff, but probably not a lot. |
Implementing the IPC stuff ourselves isn't actually too hard. Libraries are opaque, too generic, include stuff that we don't need, may contain bugs, and are possibly incompatible with our threat model (sandboxed arbitrary code execution). For serialization, we have similar security constraints as with our network packets. I'd therefor be careful with serialization libs.
Btw. I've prettified my futex code stuff enough that you can take a look: https://gist.github.com/Desour/5351f1fe74a88f539ab909b6a7a254ed To decide what our IPC thing needs, we also need to look at what messages we have:
(Apart from asynchronous calls) I think we can satisfy our needs with one synchronous call channel and two queues for large data:
Asynchronous calls probably need a ringbuffer. It can also have its own pipe for its large data. If the ringbuffer is filled, we can wait for it to become empty by doing a synchronous call. About transfer/serialization of specific things:
All (or most) caches need to be cleared at callback exit. |
I think it would be possible to avoid the fallback pipes in synchronous communication like so: When one end needs to send large amounts of data, it sets some flag in the message (indicating that there is more data to send) and waits for a synchronous acknowledgment from the other end. At this point it can send another message with more data. This process repeats until all the data is sent. IMO the initial implementation should use only synchronous communication. The performance should be OK as long as the message passing is fast. Afterward, asynchronous communication can be added. It is possible that the speedup will not be great if synchronous and asynchronous calls are interspersed. |
Nice idea. Btw. I've tried doing IPC channels with memfd (for execve): https://github.com/Desour/mt_futex_ipc/blob/master/mt_futex_ipc.cpp (Feel free to copy stuff.) Just doing synchronous calls (and doing potential asynchronousable calls synchronously) is probably fine for now. |
4c7d017
to
cb78d6a
Compare
(Windows is also a common platform)
This sounds flawed to me again. Callbacks are strictly ordered because whatever things can happen inbetween them. I think we should start simple (callback-reply, like how the PR is now) and see if adding different queues or asynchronicity can be beneficial later. |
Log timestamps aren't working.
863902b
to
21c2496
Compare
Good news: Android is basically working. That means the first step is done, though the code has not been reviewed. I put some bug fixes in later steps. |
7518097
to
6f1fc1a
Compare
This matches how posix_spawn is used.
At this point the code so far is ready for review/testing (if people want to review this huge PR in smaller parts.) Here are some tips:
|
could someone pick this up, please? |
SSCSM code is executed in a separate process. Process sandboxing is done on Linux with seccomp and on Mac with Seatbelt. Windows uses the Windows Integrity Mechanism, but I don't know if it's working. No sandboxing is done for other platforms yet. The SSCSM process is designed to be restartable in the event of an error.
SSCSM TODO list (not all in this PR)
Other system sandboxes, e.g. BSD(Can just disable SSCSM here.)get_node
/get_node_or_nil
l_util.cpp
)core.localplayer
core.camera
core.ui.minimap
l_env.cpp
l_client.cpp
set_node
/add_node