Вызов Marshal.GetHRForException в среде с частичным доверием (SecurityPermission) - PullRequest
1 голос
/ 12 июля 2009

У меня есть некоторый код ввода-вывода, который читает поток в try..catch. Он перехватывает IOException и вызывает System.Runtime.InteropServices.Marshal.GetHRForException () в рамках улова, в попытке предпринять различные действия на основе HResult. Примерно так:

try 
{
    stream.Read(...);
}
catch (IOException ioexc1)
{
   uint hr = (uint) Marshal.GetHRForException(ioexc1);
   if (hr == ...) 
      do_one_thing();
   else
      do_another();
}

Сборка подписана и помечена AllowPartiallyTrustedCallersAttribute .

Но, запустив этот код в ASP.NET с trust = "medium", я получаю следующее исключение:

Request for the permission of type 'System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.

Пара вопросов:

  1. Я думаю, что исключение происходит, потому что GetHRForException вызывает неуправляемый код, что недопустимо при среднем доверии. Правильно?
  2. Это исключение выдается не во время выполнения GetHRForException, а во время выполнения метода JIT - правильно? (Трассировка стека показывает мой метод, но я на 99% уверен, что исключение ввода-вывода не произошло)
  3. Если это так, могу ли я изменить поведение в среде с частичным доверием, чтобы я не вызывал GetHRForException (неуправляемый код) там, где это не разрешено? Другими словами, как я могу позволить JIT успешно выполняться во время компиляции, а также оценивать во время выполнения, должен ли код вызывать GetHRForException ()? Примерно так:

catch (IOException ioexc1)
{
   if (!OkToCallUnmanaged())
       throw ioexc1; 

   uint hr = (uint) Marshal.GetHRForException(ioexc1);
   if (hr == ...) 
      do_one_thing();
   else
      do_another();
}

Я думаю, что есть механизм времени выполнения для проверки наличия разрешений, но они не смогли его найти.


РЕДАКТИРОВАТЬ : эта статья блога ответ? ShawnFa из Microsoft говорит, что вы не можете сделать попытку ... поймать (SecurityException) вокруг метода, защищенного LinkDemand. Если MethodA () вызывает MethodB (), а MethodB () помечается LinkDemand для полного доверия, то LinkDemand проверяется с помощью MethodA Jit'ed. Поэтому, чтобы избежать исключения SecurityException, мне нужно извлечь Marshal.GetHRForException в отдельный метод. Это верно?

Применительно к моему коду, MethodA () может быть кодом, который вызывает Read, а затем в перехвате пытается вызвать GetHRForException (). GetHRForException - это MethodB (). LinkDemand оценивается, когда MethodA () является JIT'd. (Этот LinkDemand не выполняется в моем сценарии ASP.NET со средним уровнем доверия). Если я переместлю GetHRForException в новый метод, MethodC (), и условно вызову MethodC () только после императивного разрешения. Функция Demand () теоретически должна быть в состоянии избежать исключения SecurityException во время JIT, поскольку MethodC () JIT'd только после разрешения. Demain () успешно.

Ответы [ 3 ]

3 голосов
/ 20 июля 2009

Требуемый метод: SecurityPermission.IsUnrestricted () . Возвращает значение true или false, указывающее, разрешено или нет разрешение. Он не требует разрешения, как SecurityPermission.Demand () . Я использую IsUnresticted с SecurityPermissionFlag.UnmanagedCode, чтобы увидеть, разрешено ли сборке вызывать неуправляемый код, а затем вызывать неуправляемый код, только если это разрешено.

Есть еще один поворот. JIT-компилятор при компиляции метода проверяет наличие CodeAccessPermission LinkDemands для любого метода, называемого my метод, который нужно скомпилировать. Marshal.GetHRForException () помечается с помощью LinkDemand. Следовательно, мой метод, который вызывает Marshal.GetHRForException (), будет генерировать неуловимое SecurityException во время JIT-компиляции при запуске в ограниченной среде, такой как ASP.NET со средним доверием. Поэтому мы никогда не должны JIT метод, который вызывает Marshal.GetHRForException () в этом случае, что означает, что мне нужно разбить Marshal.GetHRForException () на отдельный метод в моем коде, который вызывается (и, следовательно, JITted), только когда UnmanagedCode без ограничений.

Вот пример кода:

internal void DoTheThing()
{
    try
    {
        DoSomethingThatMayCauseAnException();
    }
    catch (System.IO.IOException ioexc1)
    {
        // Check if we can call GetHRForException, 
        // which makes unmanaged code calls.
        var p = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
        if (p.IsUnrestricted())
        {
            uint hresult = _HRForException(ioexc1);
            if (hresult == 0x80070021)  // ERROR_LOCK_VIOLATION
                TakeActionOnLockViolation();  // maybe notify the user
            else
                throw new Exception("Cannot handle", ioexc1);
        }
        else
        {
            // The permission is restricted. Therefore, we cannot call
            // GetHRForException, and cannot do the subtle handling of
            // ERROR_LOCK_VIOLATION.  Just bail.
            throw new Exception("Cannot handle", ioexc1);
        }
    }
}

// This method must remain separate, and must not be marked with a LinkDemand for
// UnmanagedCode.
//
// Marshal.GetHRForException() is needed to do special exception handling for
// the read.  But, that method requires UnmanagedCode permissions, and is marked
// with LinkDemand for UnmanagedCode.  In an ASP.NET medium trust environment,
// where UnmanagedCode is restricted, will generate a SecurityException at the
// time of JIT of the method that calls a method that is marked with LinkDemand
// for UnmanagedCode. The SecurityException, if it is restricted, will occur
// when this method is JITed.
//
// The Marshal.GetHRForException() is factored out of DoTheThing in order to
// avoid the SecurityException at JIT compile time. Because _HRForException is
// called only when the UnmanagedCode is allowed, .NET never
// JIT-compiles this method when UnmanagedCode is disallowed, and thus never
// generates the JIT-compile time exception.
//
private static uint _HRForException(System.Exception ex1)
{
    return unchecked((uint)System.Runtime.InteropServices.Marshal.GetHRForException(ex1));
}
1 голос
/ 12 июля 2009
  1. Да - среднее доверие не разрешает вызовы в неуправляемый код. Единственный уровень доверия, который позволяет это, - полное доверие.
  2. Это зависит. Требования CAS могут предъявляться во время выполнения, но среда хостинга также может блуждать и искать то, чего не может делать.
  3. Вы можете проверить, можно ли сделать вызов неуправляемому коду, используя запрос CAS с экземпляром SecurityPermission .

Код для запроса CAS выглядит следующим образом

SecurityPermission permission = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
try
{
  permission.Demand();
  // Got it, away we go
}
catch (SecurityException)
{
  // Could not get permission to make unmanaged code calls
  // React accordingly.
}
0 голосов
/ 13 июля 2009

Получает ли System.Runtime.InteropServices.Marshal.GetHRForException () что-либо, что вы не можете получить, проверяя тип исключения? Похоже, он использует статическое отображение.

...