Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shouldn't allow randomly placing ByRef-like fields #12842

Closed
MichalStrehovsky opened this issue Jun 10, 2019 · 5 comments
Closed

Shouldn't allow randomly placing ByRef-like fields #12842

MichalStrehovsky opened this issue Jun 10, 2019 · 5 comments
Assignees
Milestone

Comments

@MichalStrehovsky
Copy link
Member

We should probably prevent loading types that attempt to place byref-like fields at addresses that are not divisible by pointer size.

[StructLayout(LayoutKind.Explicit)]
ref struct RefStruct
{
    [FieldOffset(0)]
    public short X;
    [FieldOffset(2)]
    public Span<int> Y;
}

class Program
{
    static RefStruct GetRefStruct()
    {
        return new RefStruct { Y = new int[1] { 42 } };
    }

    static void Collect()
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
    }

    static void Main()
    {
        for (int i = 0; i < 1000; i++)
            GC.KeepAlive(new int[8]);
        RefStruct a = GetRefStruct();
        a.X = 1234;
        for (int i = 0; i < 1000; i++)
            GC.KeepAlive(new int[8]);

        Collect();
        Console.WriteLine(a.X);
        Console.WriteLine(a.Y[0]);
    }
}

This will print 1234/0 instead of 1234/42 (that gets printed if you remove the StructLayout(Explicit)).

Found while messing around with dotnet/coreclr#25056.

@jkotas
Copy link
Member

jkotas commented Jun 11, 2019

If somebody does this, it will lead to silent intermittent data corruptions that are always incredibly painful to debug. How hard is this to fix? I think it would be worth fixing for 3.0.

@MichalStrehovsky
Copy link
Member Author

If somebody does this, it will lead to silent intermittent data corruptions that are always incredibly painful to debug. How hard is this to fix? I think it would be worth fixing for 3.0.

I just did a "file & forget" and didn't actually look at how difficult it is to fix.

I'll move it to 3.0 and have a look.

Cc @sergiy-k

@MichalStrehovsky MichalStrehovsky self-assigned this Jun 11, 2019
MichalStrehovsky referenced this issue in dotnet/coreclr Jun 18, 2019
First approximation of a fix for #25057.

This has two problems:
* We're checking for any byref-like typed fields. Types that don't actually contain interior pointers but were marked as `ref struct` will fail to load when not aligned properly.
* We're not doing the deep validation that we do for reference types to make sure the `ByReference<T>` field doesn't overlap with another non-byreference field.

Question is whether we're okay with those limitations, or whether we need a better fix. Better fix would likely entail inefficiently walking over the fields à la `FindByRefPointerOffsetsInByRefLikeObject` (doing the more efficient thing that we do for object references below would require a GCDesc representation of byrefness).

Contributes to #25057.
@MichalStrehovsky
Copy link
Member Author

I fixed part of this bug described in the initial issue, but this still has a leftover piece:

We currently don't check for people overlaying byref-like things with other things. So:

[StructLayout(LayoutKind.Explicit)]
ref struct Broken
{
    [FieldOffset(0)]
    Span<int> _foo;
    [FieldOffset(0)]
    IntPtr _bad;
}

Will load just fine, even though we would have blocked loading this if the ByReference<T> field was actually a reference type.

It seems blocking this might be a good thing. We can either block this by specifically looking for ByReference<T> fields recursively, or David suggested we could just disallow overlaying byref-like structs with anything (we don't dig into the byref-like struct to find ByReference<T> fields and instead treat the whole struct as something that cannot be unioned with anything). The latter would be a more breaking change.

One argument for not doing anything for this is that the GC doesn't actually mind if the interior pointer points at junk. So maybe we don't need to block this...

Thoughts?

Cc @davidwrighton @jkotas

@jkotas
Copy link
Member

jkotas commented Jun 18, 2019

We currently don't check for people overlaying byref-like things with other thing

I do not have a strong opinion on this. I would be ok with doing nothing.

@MichalStrehovsky
Copy link
Member Author

I do not have a strong opinion on this. I would be ok with doing nothing.

Happy to close then - this part was grey zone for me. The part we really cared about was fixed with dotnet/coreclr#25200.

@msftgits msftgits transferred this issue from dotnet/coreclr Jan 31, 2020
@msftgits msftgits added this to the 3.0 milestone Jan 31, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 13, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants