Hosting AddIns - The problem with untrusted code
Let us assume that we need to load some code, potentially un-trusted / hostile into our application. The runtime has a lot of functionality to offer us in this regard, we can segregate the strange code pretty well in a low privilege AppDomain, but we can't protect ourselves from the AddIn killing us.
There are two simple ways an AddIn can kill the host application, without the host being able to do something about it. Both are very easy to do by mistake, unfortunately.
The first is to simply throw an unhandled exception from a thread and the second is to cause a stack overflow.
Both are considered as fatal errors and can not be recovered. In fact, in the stack overflow case, you can't ever get any information about what exactly happened. Here is a simple malicious addin that cannot be recovered or traced:
public class MaliicousAddin : MarshalByRefObject { public void Execute() { new Thread(delegate(object obj) { Overflow(); }).Start(); } private void Overflow() { Overflow(); } }
This puts us in somewhat of a problem. We want to have a way to recover from malicious (or badly written) add ins. There are two ways that I have found to do so, both are unsatisfactory. The first is to simply run the AddIns in another process, which means that the add in can still crash the process, but the host is actually running on another process, and so not affected. The second is to play with the CRL Hosting API in order to modify the default runtime behavior. This option is probably out if you plan on hosting your application somewhere (like IIS, for example).
Any other options?
Comments
Can you stop this?
while (true) {}
BTW your 'remember me' function doesn't seem to work. I've left several comments here and I'm never remembered.
Rik,
About remember me, please tell Phil Haack about it :-)
Something like :
while(true) {}
Is very easy to stop by setting a timeout for the action to be done.
Kathleen Dollard has a post's regarding the System.AddIn for these scenario's, may be worth a look: http://msmvps.com/blogs/kathleen/archive/2008/01/14/system-addin-scenarios.aspx
http://msmvps.com/blogs/kathleen/archive/2008/01/03/why-you-care-about-system-addin.aspx
Mike,
System.AddIns doesn't touch this issue.
This is a runtime limitation
Ah, I see, separate thread.
I'd tell Phil Haack, but I want MVC.NET out as quickly as possible :)
Office has the concept of an AddIn shim - you load the AddIn in a seperate AppDomain.
This way you can unload the addin if necessary (you cannot unload assemblies from an app domain, but you can take down whole app domains) and it isolates issues as you mention from taking down the procfess.
Quoting MSDN:
"Use application domains to isolate tasks that might bring down a process. If the state of the AppDomain that's executing a task becomes unstable, the AppDomain can be unloaded without affecting the process. This is important when a process must run for long periods without restarting. You can also use application domains to isolate tasks that should not share data."
Oh and I'm pretty sure System.AddIn supports this.
Found this googling:
http://blogs.msdn.com/clraddins/archive/2007/03/05/new-system-addin-features-in-march-s-orcas-ctp.aspx
It says:
"We've now made it possible for hosts to activate add-ins in a seperate process with the same ease as in a seperate AppDomain. We're very excited about this feature because we weren't sure we could get it into Orcas and now that it's here and you can activate add-ins in their own processes you can use existing OS features to gain better reliability in the face of misbehaving add-ins: you can ensure that the host never crashes when the add-in does, limit the amount of memory available to each add-in process, and even lower the priority of these process to protect against CPU hogs."
If you make your add-in interface a wcf service interface, you could run it in it's own process and marshal the calls across named pipes..assuming your design isn't too chatty..
You get process isolation with a lot of other features, such as tracing and the ability to control activation. If you are concerned with a add-in developer writing an overflow, you might also be concerned with a developer writing code that's not thread-safe. wcf can allow you to deal with this with little fuss. The process boundary is really a good thing here. It serves as a security boundary as well (unless you are ok with add-in code mucking around inside the running application). You get all that plus you can still flow a transaction around the code inside the add-in--if it's running inside the scope of a database transaction. Let's also not forget that you may not want the code inside the add-in running with the same level of system priviledges as the host application. In the wcf scenario, you can isolate the identities of the host and add-in processes. This would allow you to run the add-in under a low priveleged windows account.
I could go on, but I think you get the point.. ;-)
Ignore System.AddIn and go straight for System.ServiceModel
Eran,
Nice, I didn't know that System.AddIns could run it on another process.
Eran,
Not good, actually.
If I am using AppDomain isolation, I can't protect from crashing AddIns.
If I am using Process isolation, I have to give the add in full trust
Evan,
This is a good thing to remember, but it doesn't work in several scenarios: UI being the foremost of them.
High level of interactions with the addins (in fact, an application that is mostly composed of addins) is also problematic.
I think that you can get away with that, probably. But it is something that I would have to try
I don't understand the need to protect against malicious addins. In what scenario is there a malicious or untrusted addin that you want to load? Why would you load it if you did not trust it?
Perhaps it can be treated as a problem of authentication or certification (apply rules at the point that you accept the addin rather than at the point where it starts misbehaving).
A separate AppDomain is exactly what protects you from AddIn crushing. A faulty addin will only take out itself (its AppDomain) and not the main application and the other AddIns.
If you take Office as an example, in the past it used a single AppDomain for all managed addins - which means that if one AddIn failed it'll take out the rest too.
So instead of using Office's default managed AddIns people started writing their own AddIn shims - an unmanaged addin that simple starts, load the CLR and an AppDomain with their managed AddIn.
This way they were protected (shimmed) from other addins.
The new VSTO does that for every addin you write, automatically (so no need for C++ code to write office addins)
Steve,
Assume that I want to load a DLL from a 3rd party, I don't know what is in there, it may be badly written or malicious.
I want to protect myself from that.
Consider any application that is using add ins from separate source. You don't want a bad addin killing the app
Eran,
Office is an unmanaged app that does its own CLR hosting.
The two ways that I mentioned will take down the _process_, not the app domain.
SubText is annoying me -- the CAPTCHA code expired and now it either fails to work or there is a delay to posting. This complaint will verify whether there is a moderator-delay to posting.
@ayende
Why not combine these? Launch a process that launches a low-priority AppDomain. Yes, it seems a bit clunky, but a thin layer of framework or whatever should make it easy...
What is your hosting environment? ASP.NET should report a stackoverflow in the event log. In your code that wires up the addin, record which virtual application and process id it is running under. You can then parse the event log for that info. Would the stacktrace record which assembly it happened in?
Setting the following in your app.config should prevent unhandled exceptions from managed threads (either from the thread pool or by creating a new thread) to take down the application:
<runtime>
</runtime>
You should really read this excellent post by Alois Kraus:
http://geekswithblogs.net/akraus1/archive/2006/10/30/95435.aspx
João,
I am more concerned with stability of the host app than the error reporting, but thanks for that.
Omer,
Didn't know that.
Very good to know, I'll check the post
Oren, have you tried specifying the security level/permissionSet when activating the AddIn token? Some AddIn resources for you to look at (inc. my screencast which briefly shows an addin out-of-proc with "Internet" security) from here:
http://blogs.msdn.com/clraddins/pages/info.aspx
As an aside, Office addins now (VSTO v3.0) actually use the MAF ;-)
What is MAF?
Comment preview