2015-06-12 2 views
1

TL; DR: Как вы изолируете исключения надстройки от убийства основного процесса?Изолировать исключения, брошенные в AppDomain, чтобы не сбой приложения

Я хочу иметь очень стабильное приложение .Net, которое работает с менее стабильным кодом в AppDomain. Это, по-видимому, является одним из главных целей AppDomain в первую очередь (ну, это и безопасная песочница), но он, похоже, не работает.

Например, в AddIn.exe:

public static class Program 
{ 
    public static void Main(string[] args) 
    { 
     throw new Exception("test") 
    } 
} 

Called в моей 'стабильный' код с:

var domain = AppDomain.CreateDomain("sandbox"); 
domain.UnhandledException += (sender, e) => { 
    Console.WriteLine("\r\n ## Unhandled: " + ((Exception) e.ExceptionObject).Message); 
}; 
domain.ExecuteAssemblyByName("AddIn.exe", "arg A", "arg B") 

Исключение брошено в AppDomain получает передается прямо в приложения, создавшего домен. Я могу зарегистрировать их с помощью domain.UnhandledException и поймать их в приложении-оболочке.

Однако, есть более проблемные исключения брошенных, например:

public static class Program 
{ 
    public static void Main(string[] args) 
    { 
     Stackoverflow(1); 
    } 

    static int Stackoverflow(int x) 
    { 
     return Stackoverflow(++x); 
    } 
} 

Это бросает StackOverflow исключения, которое убивает все приложение каждый раз. Он даже не срабатывает domain.UnhandledException - он просто подходит для убийства всего приложения.

Кроме того, призывающей вещь, как Environment.Exit() внутри AppDomain также убить родительское приложение, не проходит GO, не собирать £ 200 и не запускать любые ~Finialiser или Dispose().

Похоже, что AppDomain принципиально не выполняет то, что он утверждает (или в аренду, что, по-видимому, требует), поскольку он просто передает все исключения прямо в родительский домен, делая его бесполезным для изоляции и довольно слабый для любой безопасности (если я могу вынести родительский процесс, я могу, возможно, поставить под угрозу работу машины). Это было бы довольно фундаментальным сбоем в .Net, поэтому я должен что-то пропускать в своем коде.

Я что-то упустил? Есть ли способ сделать AppDomain действительно изолировать код, который он запускает и выгружает, когда что-то происходит плохо? Я использую неправильную вещь и есть ли какая-то другая функция .Net, которая обеспечивает изоляцию исключений?

+0

Ну, просто потому, что ваши сбои приложений не обязательно означают, что в .NET существует фундаментальный недостаток. Может, у вашего кода что-то не хватает? https://msdn.microsoft.com/en-us/magazine/cc163701.aspx – MickyD

+0

@ MickyDuncan вы прочитали нижнюю часть вопроса? Это именно то, что я прошу! – Keith

ответ

4

Я брошу на некоторых случайных мыслях, но что @Will сказал правильны в отношении разрешений, CAS, прозрачность безопасности и песочница. AppDomains не совсем супермен. Что касается исключений, хотя AppDomain способен обрабатывать самые необработанные исключения. Категория исключений, которые они не являются, называется асинхронным исключением . Поиск документации на таких исключений является немного более трудным теперь, когда мы имеем асинхронной/Await, but it exists, и они приходят в трех общих формах:

  • StackOverflowException
  • OutOfMemoryException
  • ThreadAbortException

Эти исключения называются асинхронными, поскольку они могут быть сброшены в любом месте, даже между кодами операций CIL. Первые два касаются смерти всей окружающей среды. CLR не обладает полномочиями Феникса, он не может справиться с этими исключениями, потому что средства для этого уже мертвы. Обратите внимание, что эти правила существуют только тогда, когда CLR их бросает. Если вы просто новый и экземпляр и бросаете его сами, они ведут себя как обычные исключения.

Sidenote: Если вы когда-нибудь взглянуть на дамп памяти процесса, на котором размещена CLR, вы будете видеть, что есть всегдаOutOfMemoryException, ThreadAbortException и StackOverflowException в куче, но у них нет корней вы можете см., и они никогда не получат GCed. Что дает? Причина, по которой они существуют, заключается в том, что CLR предопределяет их - они не смогут выделить их в то время, когда они необходимы. Он не сможет выделить OutOfMemoryException, когда у нас не хватает памяти.

Существует часть программного обеспечения, которое может обрабатывать все эти исключения. Начиная с 2005 года SQL имеет возможность запускать сборки .NET с помощью функции SQLCLR. SQL-сервер - довольно важный процесс, и с сборкой .NET сборка OutOfMemoryException, и это сведение всего процесса SQL показалось крайне нежелательным, поэтому команда SQL не позволяет этому случиться.

Они делают это с использованием функции .NET 2.0 под названием с ограниченным исполнением и критическими областями. Вот где такие вещи, как ExecuteCodeWithGuaranteedCleanup, вступают в игру. Если вы можете самостоятельно разместить CLR, начните с собственного кода и самостоятельно создайте CLR, тогда вы сможете изменить политику эскалации: из собственного кода вы сможете обрабатывать эти управляемые исключения. Именно так SQL CLR обрабатывает эти ситуации.

6

Вы ничего не можете сделать о Environment.Exit(), так же как вы не можете помешать пользователю убить ваш процесс в диспетчере задач. Статический анализ для этого также можно обойти. Я бы не слишком беспокоился об этом. Есть вещи, которые вы можете сделать, и то, что вы действительно не можете.

AppDomain делает то, что он требует. Однако, что это на самом деле требует делать и что вы считаете претензий делать это две разные вещи.

Необработанные исключения в любом месте снимут ваше приложение. AppDomains не защищает их.Но вы можете предотвратить необработанные исключения из пересекающей границы AppDomain следующей (К сожалению, нет коды)

  1. Создать AppDomain
  2. нагрузки и разворачивать контроллер плагин в этом AppDomain
  3. плагин управления через этот контроллер , который
  4. Изолирует вызовы сторонних плагинов, обертывая их в блоки try/catch.

Действительно, единственное AppDomain дает вам это способность нагрузки, изолировать и выгружать сборки, которые вы не полностью доверяете во время выполнения. Вы не можете сделать это в пределах исполняемого AppDomain. Все загруженные сборки остаются до тех пор, пока выполнение не прекратится, и они пользуются тем же разрешением, что и все остальные коды в AppDomain.


Чтобы быть сенсорным понятнее, вот некоторые псевдокоды, который выглядит как C#, который предотвращает код третьей стороны из выбрасывающих исключений через границу AppDomain.

public class PluginHost : IPluginHost, IPlugin 
{ 
    private IPlugin _wrapped; 
    void IPluginHost.Load(string filename, string typename) 
    { 
     // load the assembly (filename) into the AppDomain. 
     // Activator.CreateInstance the typename to create 3rd party plugin 
     // _wrapped = the plugin instance 
    } 

    void IPlugin.DoWork() 
    { 
     try 
     { 
      _wrapped.DoWork(); 
     }catch(Exception ex) 
      // log 
      // unload plugin whatevs 
     } 
} 

Этот тип будет создан в вашем Plugin AppDomain, и его прокси развернула в AppDomain приложения. Вы используете его для кукольного плагина в Plugin AppDomain. Он предотвращает исключения из пересечения границ AppDomain, выполняет задачи по загрузке и т. Д. И т. Д. Вытягивание прокси-типа типа плагина в приложение AppDomain очень рискованно, так как любые типы объектов, которые НЕ являются маршалByRefObject, что прокси-сервер может каким-то образом попасть в ваши руки (например, Throw new MyCustomException()) приведет к сборке плагина, загружаемому в приложение AppDomain, что сделает ваши попытки изоляции недействительными.

(это немного упрощенный)

+0

Я действительно не ожидаю, что смогу остановить пользователя, но я ожидаю, что смогу защитить свое приложение от кода, запущенного в песочнице, - это будет не так много песочницы. Я думаю, что изоляция и песочница не являются тем, что должны делать AppDomains, но без песочницы безопасность может быть нарушена. Разве это не делает их бессмысленными? Я должен доверять коду, чтобы обрабатывать все его исключения, но я не верю, что он записывал на диск? Что мне не хватает? – Keith

+1

@ Хорошо, вы * можете * защитить свой код с помощью AppDomains, но защита ** ограничена **. Они не бессмысленны, так как я сказал, что вы можете контролировать CAS, загружать/выгружать сборки (разгрузочные сборки большие!) И изолировать сторонний код, используя их. Они не * супермен *, но они не * aquaman *. И вам не нужно доверять «коду», чтобы «обрабатывать исключения», так как вы можете легко предотвратить необработанные исключения, пересекая границу AppDomain (не включая unatchables). Я не верю, что вам не хватает преимуществ, потому что вы видите только то, что не дает вам. – Will

+3

Есть несколько исключений, которые не воспроизводятся обычными правилами. Большие три - это StackOverflowException, OutOfMemoryException и ThreadAbortException. Они существуют за пределами того, что может обрабатывать AppDomain/Thread. Единственный способ разумно справиться с этими случаями - разместить CLR самостоятельно и использовать [ограниченное выполнение] (https://msdn.microsoft.com/en-us/library/ms228973 (v = vs.110) .aspx) и надежность контракты. Именно так SQLCLR может защитить себя от CLR от OOMing, не отнимая при этом SQL-процесс. – vcsjones