为什么这段代码没有unsafe关键字?

时间:2009-04-27 08:59:47

标签: c# .net unsafe

an answer到他自己的controversial questionMash表明您不需要“unsafe”关键字来直接读取和写入任何.NET对象实例的字节。您可以声明以下类型:

   [StructLayout(LayoutKind.Explicit)]
   struct MemoryAccess
   {

      [FieldOffset(0)]
      public object Object;

      [FieldOffset(0)]
      public TopBytes Bytes;
   }

   class TopBytes
   {
      public byte b0;
      public byte b1;
      public byte b2;
      public byte b3;
      public byte b4;
      public byte b5;
      public byte b6;
      public byte b7;
      public byte b8;
      public byte b9;
      public byte b10;
      public byte b11;
      public byte b12;
      public byte b13;
      public byte b14;
      public byte b15;
   }

然后你可以做一些事情,比如改变一个“不可变的”字符串。以下代码在我的机器上打印“bar”:

 string foo = "foo";
 MemoryAccess mem = new MemoryAccess();
 mem.Object = foo;
 mem.Bytes.b8 = (byte)'b';
 mem.Bytes.b10 = (byte)'a';
 mem.Bytes.b12 = (byte)'r';
 Console.WriteLine(foo);

您也可以通过使用相同的技术破坏对象引用来触发AccessViolationException

问题:我认为(在纯托管C#代码中)unsafe关键字是做这样的事情所必需的。为什么这里没有必要? 这是否意味着纯粹的托管“安全”代码根本不安全?

3 个答案:

答案 0 :(得分:12)

好的,这是令人讨厌的...使用工会的危险。这可能有用,但不是一个好主意 - 我想我会将它与反射(你可以做大多数事情)进行比较。我有兴趣看看这是否适用于受限制的访问环境 - 如果是这样,它可能代表一个更大的问题......


我刚刚测试它没有“完全信任”标志,运行时拒绝它:

  

无法加载“MemoryAccess”类型   从程序集'ConsoleApplication4,   版本= 1.0.0.0,文化=中立,   PublicKeyToken = null'因为对象   重叠在偏移0和   装配必须是可验证的。

要拥有这面旗帜,你已经需要高度信任 - 所以你已经可以做更多令人讨厌的事了。字符串是一个稍微不同的情况,因为它们不是正常的.NET对象 - 但是还有其他一些方法可以改变它们 - 但“联合”方法是一个有趣的方法。对于另一种hacky方式(有足够的信任):

string orig = "abc   ", copy = orig;
typeof(string).GetMethod("AppendInPlace",
    BindingFlags.NonPublic | BindingFlags.Instance,
    null, new Type[] { typeof(string), typeof(int) }, null)
    .Invoke(orig, new object[] { "def", 3 });
Console.WriteLine(copy); // note we didn't touch "copy", so we have
                         // mutated the same reference

答案 1 :(得分:5)

哎呀,我跟unsafe混淆了fixed。这是一个更正版本:

示例代码不需要使用unsafe关键字进行标记的原因是它不包含指针(请参阅下面的报价,了解为什么这被认为是不安全的)。你是完全正确的:“安全”可能更好地被称为“运行时友好”。有关此主题的更多信息,请参阅Don Box和Chris Sells Essential .NET

引用MSDN,

  

在公共语言运行库(CLR)中,   不安全的代码称为   无法验证的代码。 C#中的不安全代码   不一定是危险的;它是   只是安全不可能的代码   由CLR验证。 CLR会   因此,只有执行不安全的代码   它是一个完全可信的组件。如果   你使用不安全的代码,这是你的   有责任确保你的   代码不会带来安全风险   或指针错误。

固定和不安全之间的区别在于固定阻止CLR在内存中移动东西,因此运行时之外的东西可以安全地访问它们,而不安全则恰恰相反的问题:CLR可以保证正确对于一个dotnet引用的分辨率,它不能为指针执行此操作。你可能还记得各种关于引用如何不是一个指针的微软,这就是为什么他们对一个微妙的区别做了如此大惊小怪。

答案 2 :(得分:0)

你仍然选择退出'托管'位。有一个潜在的假设,如果你能做到那么你知道你在做什么。