All right, let's settle down.
We have Merve Goulmez.
She's going to talk about friend or foe, exploring in process isolation to maintain
memory safety for unsafe rust.
Thank you very much.
Take it away.
Hello, everyone.
I am happy to be here.
Let's get started.
I hope that one is working now.
As you see, previous presentation I talked about is uptake of rust and EtoB project,
for example, rust for Linux or Mozilla or currently is happening is rust in Windows OS.
For example, Mozilla is now 11% is rust and the other different languages, for example,
C and C++.
Of course, that one is all virtual developments, requires mixed language application.
Also today we saw previous talk.
They talk about unsafe rust.
Rust has actually highest two languages.
One of them is safe and the other one is unsafe rust.
And unsafe rust doesn't enforce memory safety guarantees and why we need it.
Sometimes we want to do some low level control or implementation details or sometimes we
need it for optimization.
In Cherry Talk, they did really demo here and unsafe rust can violate completely rust
application memory safety.
They can do different route pointers or they can allow us to call unsafe functions via
foreign function interface.
Academic work shows that more than 72% of rust created is dependent on at least one unsafe
FFI.
Now we have two things, safe rust and unsafe rust.
Unsafe rust says trust me, I know what I am doing.
Should we do trust or should we do something and put on shield?
And the gap is here actually.
I always mention this mixed language application undermines memory safety guarantee of safe
rust.
And as a result, isolation is really needed.
And I am a PhD student.
I am a researcher.
A lot of academic work to address this issue, we have a lot of academic work for example
airing, trust, sun crust or so on.
But what is the difference between these different academic publications?
They either say that okay, we should use process-based isolation or we should use in process isolation.
When you have process-based isolation, firstly you have integrity.
It means that each processor, I mean it is on virtual memory space and also the other
nicest things you have resilience.
It means that each processor, it is on failure boundary.
And if one process is crash, the other one is not affected.
And a good example for that one, multiprocess software architecture.
And the other side, we have in process isolation.
It means that you have one other space and inside this one other space, how we can isolate
one part of it.
For example, you want to protect just a key or you want to protect one part of applications.
Of course, if you have in process isolation, it can significantly reduce the runtime cost
because context-severing compared to the traditional process isolation is lower.
And I put here early approaches.
This small box and inside the small box means that in process isolation and the other we
have just sandbox provide the process-based isolation.
And I would like to highlight something.
Just one of them is offering crash resistance, but this is process-based isolation.
We have STRAT here, but STRAT doesn't support for us, just it supports C applications.
And I did some measurement and according to this measurement, if you have process-based
isolation, actually it is 10 times higher than compared in process isolations.
But the gap is here actually can be provided best of the board wars.
It means that can we have integrity and failure boundaries similar to process-based isolations
and we want to also have overheads similar to in process isolation.
And my goal is firstly maintain the rest application safety and also we want to increase the software
resilience of rest applications and also how we can provide ease of use in the development.
In my early work, we provide some approach for C applications.
This is secure rewind and discard and this is an approach for recovering vulnerable application
after an attack is detected.
And how we achieve that stuff?
First we compartment the application in the distinct domains and we want to make sure that
a memory defect with a domain must only affect that domain memory.
This approach is relying on hardware assisted software fault-based isolation.
This is a protection key for user space.
And also how I detect it?
I use different pre-existing detection mechanism, for example, stack canneries and domain violations.
And as a result of this work, we publish some C library, SDRet library.
If you want to check out, you can scan the QR code.
Now I would like to explain the high-level idea.
We have function F and we have unsafe.
And if you write just some box on top of that, we want to have some memory safety guarantees
and we want to have some isolation.
And let's get started.
We have parent domain and we want to run this function F in another domain.
Another domain means stacks that can heap isolations.
It means that I want to run this function F in another domain.
And to run my function in the nested domain D, firstly I need to push the argument.
I need to enter domain D and pull argument from the parent domain.
And invoke F.
I am executing applications, execute the function F.
And the question is that is there something bad or not?
But we have a guarantee that now if nested domain has some memory vulnerability,
it will not affect the parent domain memory.
It means that parent domain is still secure.
And I am offering saying that you don't need to create your application, actually.
You can just continue the execution.
How I am offering, probably all of you know that this RAS says there's some API for panic.
And we run the function F in the nested domain and after that we are checking the result.
If results say that yes, you have something bad things happens
and for example I can detect stack memory overflow or domain-domain violations,
it will return error.
If everything is okay, we don't need to do anything, it will just return okay.
But the idea is that actually we are using this API, panic,
but we are actually adding a new feature.
Panic has also memory safety guarantees.
It means that when panic happens, you can still continue your execution.
And after that I explain this rewind and discard.
And if nothing happens, if we didn't detect any memory safety violations,
we will just push mutable arguments and return value and we will return parent domain.
For this whole process in high-level idea, this STRAAT API,
CAPI is already offering this pink box,
but the point for this blue box is how we can track the rest of the types of the arguments
and how we can push another domain and how we can pull it again.
And probably all of you know that we have a lot of serialization creator.
And what is serialization?
It means serialization, we should encode the data in a format,
like I just put it in a jar, and after that we should deserialize.
When we jump to the nested domain, we should deserialize.
And as a case study, I work on two RAS crates, BINCOT and ABONOMATIONS.
BINCOT is a transformed data in a common binary representation
that allows passing data between different platforms.
And only the mention that SANDCRAS is process-based isolation mechanisms
and that uses BINCOT.
But actually, we realize that BINCOT, for our cases, is redundant
because SANDCRAS and also STDF-FI, we are ingested in single platform.
And I explore ABONOMATIONS, it's based on Rust Object MemoLayout presentations,
but it is just for specific single platform.
And it doesn't store any metadata or any type systems.
It can deserialize the place without need for another cooperation.
And we realize that ABONOMATIONS is efficient and suitable for our purpose.
And we did some benchmarking.
SNAPE is a fast comparison C-library from Google,
and it designed for high-speed compression and the compression of data.
And also presented as an FFI example in the Rust books.
And what we compare, compress and uncompress random-generated data of different sizes.
And we measure execution time of each operation for different serialization creators,
like BINCOT and ABONOMATION.
I show as a demo here, when I did all stuff, I just used a sandbox macro.
And sandbox macro is ensure that this compressed function is completely run in the different domain,
and it will not affect the parent domain.
And uncompress is also here.
Here we tested with different number of bytes, and this is the execution time.
What is our lesson learned stuff?
Of course, if you have number of bytes, if it is smaller,
in process isolation approach clearly outperforms compared to process-based isolations.
Because if you have in process isolation, you will not so much overhead.
But the interesting start with later,
we realize that even for modest-sized arguments,
the context, which is not important anymore,
is dominated by data serialization method.
What you use.
And our lesson learned tree, the data serialization method can significantly impact performance,
and it is critical to optimize it for different cases.
If you are working on this serialization creator or developing,
we can talk about it, how we can improve or how we can fit our use cases.
In summary, we introduced secure rewind and discard with isolated domains for REST-FFI.
We have two goals.
Firstly, we want to protect integrity of REST application from memory safety violation in unsafe program.
The main point is that actually I would like to highlight,
we are increasing the REST application availability,
because we have a still option for if unsafe portion of our applications is the some memory safety,
we can return back.
We have option there.
And I provided REST-FFI creators, it is open source,
if you would like to try.
And what is our takeaway?
Improved isolation approach clearly outperforms compared to process-based isolations.
But other important things is that data serialization method can significantly impact the performance.
Thank you if you have a question.
Can you quickly explain how these domains actually work?
How do you enter a domain and how do you define what part of memory is part of the domain and what is outside the domain?
Of course, it is actually handled by my C-Labri before that.
I wrote it.
But for RASPEX, if you just use sandbox macro, it will automatically handle it.
But if you go into details, for each domain I will create a new stack and new heap memory area.
Early, when there is some talk about this allocator, you can specify for allocators for specific domain.
Entering a new stack, what does it mean?
Just change the stack pointer and continue execution there.
So you do a stack switch and share it to an entry point.
But the important point is to do this rewind and discard.
You should first save your execution context in a secure way.
This is the point how we can recover.
That is kind of like set-jump and long-jump style.
Yes, set-jump, long-jump, but in a secure way.
Now we have a guarantee that...
Then you use some hardware mechanism to make sure that certain domains, certain memory is only accessible.
Yes, exactly. That is true. That is completely true.
This is the install feature. We are using that one. It is lightweight. That is why.
Because you don't need system calls?
Yes, exactly.
You don't need to use a RAM trip?
Yes, exactly. You have got everything now.
First, thanks for the great talk.
When deciding which piece of memory you put in the new domain,
the global state is shared between different domains or you copy all the global states?
Current version is just supporting HIP and memory.
HIP and memory, but for the global shared, you should copy and pass it.
It is not going to be your application. You should change it.
But as a future work, I would like to support this.
How we can actually sync between different domains to global shared states?
That could be very costly.
Sharing and copying the global state could be very costly.
Yes, exactly.
For example, here also, even though I have improved the isolations,
changing arguments between domains create a lot of overheads.
Yes, this is the bottleneck now. How we can improve actually this part?
How we can pass the function argument one domain to another domain?
This is the actual cost, actually.
Second question. You copy back all the mutable arguments.
Do you use that even if they are not changed or do you do that all the time?
I am just pushing this. If they are mutable, I am pushing the argument.
But you don't check if they have been changed by the function.
If they are mutable, then you copy them back.
Yes, exactly.
So it is a static check and not a runtime check.
Thanks.
Thanks for your nice question.
Awesome.
Sorry, unfortunately that was all we had time for.
Can we give another?
Thank you to Mervin.
Thank you.