Thread overview
Question and proposal: Can D be used to compile and run untrusted plug-ins?
Dec 04, 2021
Moamen Abdelsattar
Dec 04, 2021
Dukc
Dec 04, 2021
Moamen Abdelsattar
Dec 04, 2021
Adam D Ruppe
Dec 04, 2021
Moamen Abdelsattar
Dec 04, 2021
Paul Backus
Dec 06, 2021
Kagamin
Dec 07, 2021
ManKey
Dec 07, 2021
Brian Callahan
Dec 08, 2021
max haughton
December 04, 2021

I've recently known that D functions can be marked as @safe, which makes all unsafe operations unallowed inside the function, my question is: Can this feature be used to compile and run untrusted D code safely?
I mean: Let's say We have a program written in D or C, and we want to allow the user to extend the program by writing plug-ins and compiling them into dynamic libraries (like notepadd++ plug-ins).
Now the plug-in can access all system calls and can do something malicious, but what if the plug-in is written in D and we have something like -forceSafe compiler flag (which is the proposal) that will force every function written by the user to be @safe. Now, the user can only import the application's API and use it to perform functions and can't access the system directly. Is that true?

December 04, 2021

On Saturday, 4 December 2021 at 11:33:21 UTC, Moamen Abdelsattar wrote:

>

I've recently known that D functions can be marked as @safe, which makes all unsafe operations unallowed inside the function, my question is: Can this feature be used to compile and run untrusted D code safely?
I mean: Let's say We have a program written in D or C, and we want to allow the user to extend the program by writing plug-ins and compiling them into dynamic libraries (like notepadd++ plug-ins).
Now the plug-in can access all system calls and can do something malicious, but what if the plug-in is written in D and we have something like -forceSafe compiler flag (which is the proposal) that will force every function written by the user to be @safe. Now, the user can only import the application's API and use it to perform functions and can't access the system directly. Is that true?

No, @safe isn't suitable for that. For two reasons:

1: It only guards against memory integrity violations, not against otherwise malicious system calls. You can @safely delete everything in the home or "my documents" directory, or trigger a fork bomb. To prevent this, you have to not link any system calls to the untrusted module - which is perfectly doable, though.

2: The more difficult problem. It is not bug-free enough. @safe aims to be foolproof enough to prevent corrupting memory accidently, and it has enough challenge in that. It is more difficult to be so reliable that the memory could not be corrupted even by determined attempts. Perhaps in the far future it can do that, but not now nor anytime soon.

The same can be accomplished by sandboxing the compiled binary somehow though. For example, D program compiled to WebAssembly cannot corrupt the web browser running it, just like no WebAssembly program can (modulo browser vulnerabilities).

December 04, 2021

On Saturday, 4 December 2021 at 12:18:23 UTC, Dukc wrote:

>

The same can be accomplished by sandboxing the compiled binary somehow though. For example, D program compiled to WebAssembly cannot corrupt the web browser running it, just like no WebAssembly program can (modulo browser vulnerabilities).

I'm aware of that way of sandboxing, but it's not as efficient. I mean you can't call the functions inside the plug-in directly with C calling convention. If you need to call it thousands of times, the application will be markedly slower.
Also I think that passing structures or objects with overriden virtual functions to the application will be a difficult problem due to ABI incompitability.

>

1: It only guards against memory integrity violations, not against otherwise
malicious system calls.

Yes but I think memory-safe code can't do anything outside the API. Malicious C code (that is not linked to system calls) can still use pointer arthmetric to make system calls by address, use inline assembly to make them, or write to data segment to change the application's behaviour in a certain way. Making the untrusted code memory-safe will also make it safe to execute if it's only linked to a secure API.

>

2: The more difficult problem. It is not bug-free enough.

That's disappointing, but at least it can make malicious code more difficult to write; it's better that nothing, and vulnerabilities can be fixed by time.

December 04, 2021
On Saturday, 4 December 2021 at 11:33:21 UTC, Moamen Abdelsattar wrote:
> I've recently known that D functions can be marked as `@safe`, which makes all unsafe operations unallowed inside the function

You can also mark functions @trusted, which allows all operations inside but pretends to be safe from the outside.

So even if @safe worked for what you want, it is too easy to bypass to actually rely on it. Malicious users will just make all their naughty code as trusted.
December 04, 2021

On Saturday, 4 December 2021 at 13:53:43 UTC, Adam D Ruppe wrote:

>

You can also mark functions @trusted, which allows all operations inside but pretends to be safe from the outside.

Yeah but I'm assuming the compiler will force every function written by the user to be @safe, so the user can't mark any code @trusted, only the API can.

December 04, 2021

On Saturday, 4 December 2021 at 14:08:16 UTC, Moamen Abdelsattar wrote:

>

On Saturday, 4 December 2021 at 13:53:43 UTC, Adam D Ruppe wrote:

>

You can also mark functions @trusted, which allows all operations inside but pretends to be safe from the outside.

Yeah but I'm assuming the compiler will force every function written by the user to be @safe, so the user can't mark any code @trusted, only the API can.

Even if the compiler could do this, it would not protect you from malicious code in a DLL, because an attacker could simply provide their own malicious DLL that was created using a different compiler.

To really protect against this threat, you would have to do something like embed the compiler in the host application, load the plugin as source code, compile it with the embedded compiler, and then load the resulting DLL.

Of course, if you're going to all that trouble, you may be better off embedding an interpreter for a scripting language like Lua, which is designed for this kind of use-case.

December 06, 2021

If memory serves well, long ago there was an idea of a -safe compiler switch that would restrict the language to only safe subset, but it was abandoned.

December 07, 2021

On Saturday, 4 December 2021 at 11:33:21 UTC, Moamen Abdelsattar wrote:

>

Now the plug-in can access all system calls and can do something malicious, but what if the plug-in is written in D and we have something like -forceSafe compiler flag (which is the proposal) that will force every function written by the user to be @safe. Now, the user can only import the application's API and use it to perform functions and can't access the system directly. Is that true?

You can use pure attribute. But it will still be by cast to break it

December 07, 2021

On Saturday, 4 December 2021 at 11:33:21 UTC, Moamen Abdelsattar wrote:

>

I've recently known that D functions can be marked as @safe, which makes all unsafe operations unallowed inside the function, my question is: Can this feature be used to compile and run untrusted D code safely?
I mean: Let's say We have a program written in D or C, and we want to allow the user to extend the program by writing plug-ins and compiling them into dynamic libraries (like notepadd++ plug-ins).
Now the plug-in can access all system calls and can do something malicious, but what if the plug-in is written in D and we have something like -forceSafe compiler flag (which is the proposal) that will force every function written by the user to be @safe. Now, the user can only import the application's API and use it to perform functions and can't access the system directly. Is that true?

What you want appears to be similar to the pledge(2) system call from OpenBSD:
https://man.openbsd.org/pledge.2

It is available on all D compilers, but only when running your code on OpenBSD.

~Brian

December 08, 2021

On Tuesday, 7 December 2021 at 19:31:46 UTC, Brian Callahan wrote:

>

On Saturday, 4 December 2021 at 11:33:21 UTC, Moamen Abdelsattar wrote:

>

[...]

What you want appears to be similar to the pledge(2) system call from OpenBSD:
https://man.openbsd.org/pledge.2

It is available on all D compilers, but only when running your code on OpenBSD.

~Brian

Interesting.
I must get round to trying OpenBSD soon.

What tracing infrastructure exists on OpenBSD?