C#中有趣的类型系统违规(在安全的环境中)

时间:2014-04-27 12:56:47

标签: c# .net

我所说的类型系统违规 - 是在模仿工会时。

为什么在安全的环境中允许它?

  • 它允许您处理值类型,作为不同的值类型 - 正如您将要实现的那样:

    float f = 2.5f;
    int binaryIdenticalInt = *((int *)& f);

  • 它允许您引用类型为X的对象,因为它是Y类型,即使这些类型之间没有任何关系。

  • 它允许您创建CLR甚至无法加载的类型(如果至少有一个重叠字段是引用类型,并且其中至少有一个是值/指针类型 - CLR会拒绝加载它)。我希望C#编译器比它允许的内容更加挑剔,而不是CLR。不要少。

那么,为什么编译器首先在安全的上下文中允许这样的东西呢?为什么它不需要具有显式布局的类型的不安全上下文,这些类型具有重叠的字段?

我希望那里有人有答案 - 因为它必须有趣。

示例(所有编译时都没有不安全的块,甚至不安全的开关):

[StructLayout(LayoutKind.Explicit)]
struct ValueUnion
{
    [FieldOffset(0)]
    public int i;

    [FieldOffset(0)]
    public float f;
}

[StructLayout(LayoutKind.Explicit)]
struct ReferenceUnion
{
    [FieldOffset(0)]
    public string str;

    [FieldOffset(0)]
    public Stream stream;
}

[StructLayout(LayoutKind.Explicit)]
struct CLRWontLoad
{
    [FieldOffset(0)]
    public string str;

    [FieldOffset(0)]
    public IntPtr ptr;
}

3 个答案:

答案 0 :(得分:7)

让我回答一些略有不同的问题。

  

使用显式布局属性是否允许在不使用unsafe关键字的情况下违反类型安全?

是。这就是它的用途。

  

编译器对FieldOffset属性有什么了解?

编译器验证FieldOffset属性不在static / const字段上,并且它与StructLayout属性一致。它不会检查属性的有效性。

  

为什么C#编译器没有检测到属性的潜在不安全使用并需要unsafe关键字?

这是一个功能。为了供您使用,必须考虑,设计,指定,实施,测试,记录功能并将其发送给客户。这个功能被认为是。它没有设计,指定,实施,测试,记录或发货,因此没有这样的功能。

  

这是一个令人不满意的答案。

我的建议是不要问"为什么"关于StackOverflow的问题。 "为什么"问题很模糊,因此你经常得到不满意的答案。 "为什么不"问题甚至更难以令人满意地回答,因为他们有这样的假设,即世界不应该成为一种不是它的方式的充分理由。

  

好的,让我改一下。如果这个功能适用于编译器设计团队,那么在决定是否继续使用该功能时,他们可能会考虑 的优缺点?

  • Pro:代码可以明确地以不安全的方式使用,因此应该要求unsafe关键字允许它。

  • Con:unsafe关键字是为了防止意外使用危险的不安全功能,这四十年的C已经向我们展示了即使是专业程序员也很难做到的功能 - 发现错误。没有办法意外地使用struct layout属性。可以假设使用此属性的人确切知道他们正在做什么,并且有充分的理由知道他们正在做什么。

在我看来,缺点超过了职业选手。对于适中的成本,该功能将是非常小的益处。

请记住,实施的每个功能都意味着列表中的另一个功能而不是,因为预算是有限的。我不想做这个弱功能,必须削减一个真正让开发人员受益的功能。

  

团队是否考虑过此功能并以此为由拒绝了它?

是。见

http://connect.microsoft.com/VisualStudio/feedback/details/357828/using-explicit-struct-layout-causes-compiler-to-produce-unverifiable-code

答案 1 :(得分:3)

这有点不可避免地最终成为循环解释。它允许您使用工会违反类型安全,因为这是您使用工会的原因。它不能省略该功能,因为没有它就不能写出体面的pinvoke。特别是winapi充满了工会。并且pinvoke不是不安全的,它只是无法验证。 C#是一种非常实用的语言,如果你想拍摄你的腿,那么你就有权使用。

这不是一个安全漏洞。 CLR真正关心的是可验证的代码。 C#不安全关键字只有重叠,没有关键字的C#代码也不能自动验证。使用pinvoke是最明显的例子。 CLR源代码在评论中很好地解释了它。从clr / src / vm / class.cpp,MethodTableBuilder :: HandleExplicitLayout()函数:

// 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 implment:
//
// 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()).
//

“OREF”表示“对象引用”。规则3使得kibosh写出真正不安全的代码,不可靠的代码只能完全信任。规则2拒绝你的第三个例子。那个有基本的cooties,因为GC无法猜测对象引用是否有效所以不能保持对象存活。

还有更多代码处理显式布局,例如,它还会根据参与字段类型的信任来计算最小信任。您可以查看SSCLI20发行版。

答案 2 :(得分:-2)

哦,现在你已经添加了一些例子。

编译器允许它,因为这段代码是安全的。您使用安全代码几乎可以完成使用不安全代码所做的一切。这就是为什么没有人几乎不使用不安全的代码的原因。

C#还允许您序列化一种类型并将其反序列化为另一种类型。安全并不意味着没有错误。