Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interesting type-system violation in C# (in safe context)

Tags:

c#

.net

The type system violation which I talk about - is when emulating unions.

Why is it allowed in safe context?

  • It allows you to handle value types, as different value type - as you would achieve with:

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

  • It allows you to reference an object of type X, as it was of type Y, EVEN WHEN THERE IS NO RELATION BETWEEN THOSE TYPES.

  • It allows you to create types which the CLR won't even load (if at least one of overlapping fields is a reference type, and at least one of them is value/pointer type - the CLR would refuse to load it). I would expect the C# compiler to be more picky about what it allows, than the CLR. not less.

So, why the compiler allow such things in safe context in the first place? Why doesn't it require unsafe context for types with explicit layout, which have overlapping fields?

I hope someone out there has an answer - because it has to be interesting.

Examples (all compiled without unsafe block, nor even unsafe switch):

[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;
}
like image 616
Shlomi Borovitz Avatar asked Oct 25 '25 11:10

Shlomi Borovitz


2 Answers

Let me answer some slightly different questions.

Does using the explicit layout attribute allow violations of type safety without using the unsafe keyword?

Yes. That's what it's for.

What knowledge of the FieldOffset attribute does the compiler have?

The compiler verifies that the FieldOffset attribute is not on static / const fields and that it is consistent with the StructLayout attribute. It does not check anything else about the validity of the attribute.

Why does the C# compiler not detect a potentially unsafe usage of the attribute and require the unsafe keyword?

That's a feature. In order to be used by you, a feature must be thought of, designed, specified, implemented, tested, documented and shipped to customers. This feature was thought of. It was not designed, specified, implemented, tested, documented, or shipped, and therefore there is no such feature.

That's an unsatisfying answer.

My advice is to not ask "why" questions on StackOverflow. "Why" questions are vague, and therefore you often get unsatisfying answers. "Why not" questions are even harder to answer satisfactorilly because they have the presupposition that there needs to be a good reason for the world to not be a way that it isn't.

OK, let me rephrase. If this feature were pitched to the compiler design team, what pros and cons might they consider when deciding whether to proceed with the feature?

  • Pro: the code can clearly be used in an unsafe manner, and so should require the unsafe keyword to allow it.

  • Con: the unsafe keyword is there to prevent accidental use of dangerously unsafe features, feature which four decades of C has shown us that even expert programmers make hard-to-find mistakes. There is no way to accidentally use the struct layout attribute. Someone who uses this attribute can be assumed to know precisely what they're doing, and have a good reason for what they're doing.

In my opinion, the cons outweigh the pros here. The feature would be a very small benefit for a moderate cost.

Remember, every feature implemented means another feature on the list not implemented, because the budget is finite. I would not want to do this weak feature and have to cut a feature that actually benefited developers.

Has the team considered this feature and rejected it on these grounds?

Yes. See

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

like image 101
Eric Lippert Avatar answered Oct 28 '25 00:10

Eric Lippert


This somewhat inevitably ends up as a circular explanation. It allows you to use unions to violate type safety because that's what you use unions for. It cannot omit the feature because you can't write decent pinvoke without it. The winapi in particular is rife with unions. And pinvoke is not unsafe, it is merely unverifiable. C# is a pretty pragmatic language, if you want to shoot your leg off then you're entitled to.

It is not otherwise a security hole. What the CLR truly cares about is verifiable code. The C# unsafe keyword merely has overlap, C# code without the keyword is not automatically also verifiable. With pinvoke being the most obvious example. The CLR source code explains it pretty well in a comment. From clr/src/vm/class.cpp, MethodTableBuilder::HandleExplicitLayout() function:

// 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" means "Object reference". It is rule number 3 that puts the kibosh on writing truly unsafe code, unveriable code can only run in full trust. Rule number 2 rejects your 3rd example. That one has fundamental cooties because the GC cannot possibly guess if the object reference is valid so cannot keep the object alive.

There's more code that deals with explicit layout, it for example also calculates the minimum trust based on the trust of the participating field types. You can have a look-see in the SSCLI20 distribution.

like image 32
Hans Passant Avatar answered Oct 28 '25 01:10

Hans Passant



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!