【问题标题】:Calling Marshal.GetHRForException in a partial trust environment (SecurityPermission)在部分信任环境中调用 Marshal.GetHRForException (SecurityPermission)
【发布时间】:2009-07-12 14:20:53
【问题描述】:

我有一些 IO 代码可以读取 try..catch 中的流。它捕获 IOException 并调用 System.Runtime.InteropServices.Marshal.GetHRForException() 在 catch 中,尝试根据 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 处理时 - 对吗? (stacktrace 显示了我的方法,但我 99% 确定没有发生 IO 异常)
  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();
}

我认为有一个运行时机制来测试权限是否可用,但一直找不到。


编辑this blog article 是答案吗?微软的 ShawnFa 说你不能在一个受 LinkDemand 保护的方法周围做一个 try ... catch(SecurityException)。如果 MethodA() 调用 MethodB(),并且 MethodB() 用 LinkDemand 标记为完全信任,则使用 MethodA 检查 LinkDemand is Jit'ed。因此,为了避免 SecurityException,我需要将 Marshal.GetHRForException 提取到一个单独的方法中。那是对的吗?

应用于我的代码,MethodA() 可能是调用 Read 的代码,然后在 catch 中尝试调用 GetHRForException()。 GetHRForException 是 MethodB()。当 MethodA() 为 JIT 时评估 LinkDemand。 (此 LinkDemand 在我的中等信任 ASP.NET 方案中失败)。如果我将 GetHRForException 移动到一个新方法 MethodC() 中,并且仅在命令式 permission.Demand() 成功后有条件地调用 MethodC(),理论上我应该能够在 JIT 时间避免 SecurityException,因为 MethodC() 将只有在 permission.Demain() 成功后才会进行 JIT。

【问题讨论】:

    标签: .net asp.net security code-access-security partial-trust


    【解决方案1】:

    需要的方法是SecurityPermission.IsUnrestricted()。它返回一个 true 或 false 指示是否允许该权限。它不像SecurityPermission.Demand() 那样要求权限。我使用 IsUnrestricted 和 SecurityPermissionFlag.UnmanagedCode 来查看是否允许程序集调用非托管代码,然后仅在允许时才调用非托管代码。

    还有一个额外的转折。 JIT 编译器在编译方法时,会在任何调用我要编译的方法的方法上检查 CodeAccessPermission LinkDemands。 Marshal.GetHRForException() 用 LinkDemand 标记。因此,我调用 Marshal.GetHRForException() 的方法将在 JIT 编译时抛出无法捕获的 SecurityException,当在受限环境中运行时,例如具有中等信任度的 ASP.NET。因此,在这种情况下,我们绝不能 JIT 调用 Marshal.GetHRForException() 的方法,这意味着我需要在我的代码中将 Marshal.GetHRForException() 分解为一个单独的方法,该方法仅在 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));
    }
    

    【讨论】:

      【解决方案2】:
      1. 是 - 中等信任不允许调用非托管代码。唯一允许的信任级别是完全信任。
      2. 视情况而定。 CAS 需求可以在运行时发生,但托管环境也可以四处游荡,寻找它无法做到的事情。
      3. 您可以测试是否可以通过使用带有SecurityPermission 实例的CAS 请求来调用非托管代码。

      创建 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.
      }
      

      【讨论】:

      • 好的,这是很棒的信息。这涵盖了第三季度的 b 部分。但是a部分呢?如何让 JIT 编译成功?我可以用安全属性或...标记我的方法吗?请记住,我的理论是 SecurityPermission 错误不是在运行时发生,而是在 JIT 期间发生 - 我认为您确认这是可能。那么问题来了,如何编写代码让 JIT 编译。
      • 它应该在运行时发生,否则程序集甚至不会加载 - 为此需要将程序集标记为需要权限。即使那样,这也可以说是运行时检查,因为它会在程序集加载时发生,这可能是在运行时。
      • 我认为这是相关的:blogs.msdn.com/shawnfa/archive/2006/01/11/511716.aspx 它基本上是这么说的,我认为:假设 MethodA() 调用 MethodB(),MethodB() 调用非托管代码,例如 Marshal.GetHRForException(), (这意味着 UnmanagedCode 的 LinkDemand)。在 MethodA() 被 JITted 时检查 UnmanagedCode 权限。所以为了避免JIT期间的错误,我需要通过一个方法调用将检查(permission.Demand())和调用(GetHRForException)分开。
      • 不同类型的检查,链接需求是方法上的属性,并且确实在JIT时间进行检查。它几乎被框架本身使用,并且很少在 CLR 源代码之外看到它。我展示的是一种命令式需求,而不是像 [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)] 那样的声明性需求
      • 我理解命令式与声明式,但这篇文章似乎正是我的情况。还是我错了? GetHrForException 的 LinkDemand(隐式声明)导致我的方法抛出。我可以进行命令式检查,但仅此一项并不能避免我观察到的问题。需要明确的是:它没有抛出 GetHRForException。如果我在 GetHRForException() 周围执行 if/then,它仍然会抛出。在 JIT 中!避免这种情况的唯一方法是引入另一个级别的方法调用。还是我误解了 Shawn Farkas 先生的博文?
      【解决方案3】:

      System.Runtime.InteropServices.Marshal.GetHRForException() 是否可以通过检查异常类型获得任何您无法获得的东西?看起来它使用了静态映射。

      【讨论】:

      • 它为我提供了 HResult,它不能从异常中公开获得。
      • 哪个 HResult?来自异常内部的那个?这不是固定映射吗?不是和异常类型一一对应吗?
      • 快速浏览 Reflector 表明 HResult 是由 Exception.SetErrorCode 设置的,并且始终在构造函数中设置,并且始终设置为常量值。这表明 ex.GetType() 确定 HResult,因此可以用来代替它。
      猜你喜欢
      • 1970-01-01
      • 2013-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多