2010-12-23 70 views
5

考虑下面的代码:如果更改像这样的字符串的内容会导致异常?

using System; 
using System.Runtime.InteropServices; 

namespace Demo 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      const string test = "ABCDEF"; // Strings are immutable, right? 
      char[] chars = new StringToChar{str=test}.chr; 
      chars[0] = 'X'; 

      // On an x32 release or debug build or on an x64 debug build, 
      // the following prints "XBCDEF". 
      // On an x64 release build, it prints "ABXDEF". 
      // In both cases, we have changed the contents of 'test' without using 
      // any 'unsafe' code... 

      Console.WriteLine(test); 
     } 
    } 

    [StructLayout(LayoutKind.Explicit)] 
    public struct StringToChar 
    { 
     [FieldOffset(0)] 
     public string str; 
     [FieldOffset(0)] 
     public char[] chr; 
    } 
} 

通过运行该代码,我们可以改变一个字符串的内容没有异常发生的历史。我们不必宣布任何不安全的代码就可以这样做。这段代码显然非常狡猾!

我的问题是这样的:你认为上面的代码应该抛出异常吗?

[EDIT1:请注意,其他人已经尝试这对我来说,有的人得到不同的结果 - 这是不是太suprising鉴于我在做什么的nastyness ...;)]

[ EDIT2:请注意,我使用Visual Studio 2010的Windows 7旗舰版64位]

[EDIT3:所做的测试字符串常量,这只是为了更狡猾]

+2

重复[我已经问过的问题](http://stackoverflow.com/questions/792735/why-does-this-code-work-without-the-unsafe-keyword):-) – 2010-12-23 14:43:47

+0

非常有趣!看起来这是一个众所周知的问题。 – 2010-12-23 14:50:29

回答

3

我的投票对制作FieldOffset不安全! 。

5

clr/src/vm/class.cpp,MethodTableBuilder :: HandleExplicitLayout的SSCLI20源代码可以提供一些见解。这条评论描述了规则(为便于阅读而编辑):

// go through each field and look for invalid layout 
// (note that we are more permissive than what Ecma allows. We only disallow 
// the minimum set necessary to close security holes.) 
// 
// This is what we implement: 
// 
// 1. Verify that every OREF is on a valid alignment 
// 2. Verify that OREFs only overlap with other OREFs. 
// 3. If an OREF does overlap with another OREF, the class is marked unverifiable. 
// 4. If an overlap of any kind occurs, the class will be marked NotTightlyPacked (affects ValueType.Equals()). 

规则1确保参考分配保持原子状态。规则2说明了为什么你可以做你所做的事情,任何对象类型的引用都可能重叠。不允许与值类型值重叠,这会使垃圾收集器陷入困境。规则3规定了后果,它只有使使该类型不可验证。

否则,它不是唯一的方法来拧一个字符串,而不是不安全的关键字。只需捏住一个踩住字符串的函数即可。它获取一个指向GC堆或装入堆(实际字符串)的字符串内容的指针,不会创建副本。这是无法验证的代码,并且在沙箱中运行时也是无法利用的。

驾车回家:C#不安全关键字与CLR认为可以验证的内容完全无关,因此实际上是安全代码。它处理公然的情况,使用指针或自定义值类型(固定)。这是否是C#语言规范中的泄漏是值得商榷的。 Pinvoke是更明显的边缘情况。拼命操作系统功能是非常安全的。捏造一些第三方C库不是。

但是我不得不同意@fej,[FieldOffset]应该得到了“你确定”的待遇。太糟糕了,没有语法。无可否认,我还没有想出为什么这实际上需要影响管理布局。这会使得更有意义,这个属性只适用于编组布局。奇怪的是,在早期有人拿着他的袖子,也许。