Open hamarb123 opened 6 months ago
I'm generally supportive of removing unsafe code in favor of safer patterns, particularly where newer features like ref fields
can be used to avoid the issue or special helpers like some bool IsOpportunisticallyAligned(ref T address, nuint aligment)
could exist to document the intent and centralize the logic.
Notably a lot of these places where its being used are fairly core APIs, so there is risk in making changes to them even if to remove unsafe code. It's something that we need to be extra cautious around and ensure gets the right scrutiny.
List of areas with calls to Unsafe.AsPointer
(tick indicates it's been reviewed and/or had a PR made for it):
_EnumerateConfigurationValues(Unsafe.AsPointer(ref context), &ConfigCallback);
Why do you think that this use is unnecessary? What would you replace it with (while preserving the perf characteristics). I do not see a problem with this one.
this is a extra dangerous API that can easily be used wrongly
No much different from every other Unsafe.*
API. You have to know what you are doing when using Unsafe.*
APIs.
IsOpportunisticallyAligned
Sounds good to me. I see ~5 places where this can be used.
Getting a pointer to something that is known to be pinned (many of these cases could be converted to ref fields instead
I am not convinced that it is that easy. Many of these uses have to do with low-level interop.
Tagging subscribers to this area: @dotnet/area-system-runtime-compilerservices See info in area-owners.md if you want to be subscribed.
Author: | hamarb123 |
---|---|
Assignees: | - |
Labels: | `area-System.Runtime.CompilerServices`, `untriaged`, `needs-area-label` |
Milestone: | - |
_EnumerateConfigurationValues(Unsafe.AsPointer(ref context), &ConfigCallback);
Why do you think that this use is unnecessary? What would you replace it with (while preserving the perf characteristics). I do not see a problem with this one.
_EnumerateConfigurationValues(&context, &ConfigCallback);
_EnumerateConfigurationValues(&context, &ConfigCallback);
Ok this works but it requires pragma disable.
IsOpportunisticallyAligned
Sounds good to me. I see ~5 places where this can be used.
Which file/type should I define this on?
It can be internal method on System.Runtime.CompilerServices.Unsafe
_EnumerateConfigurationValues(&context, &ConfigCallback);
Ok this works but it requires pragma disable.
Do we want to disable it globally like it was mentioned in https://github.com/dotnet/runtime/pull/80484#pullrequestreview-1244050291?
Do we want to disable it globally like it was mentioned in https://github.com/dotnet/runtime/pull/80484#pullrequestreview-1244050291?
This issue is itself about trying to reduce unsafe patterns where unnecessary. Globally suppressing the warning that you're taking a pointer to a managed object is kind-of counter intuitive to that ;)
I think we want to be explicit when we're taking a pointer to a managed object to help reduce risk and make it very apparent we're taking the necessary approaches to keep the underlying object alive, etc.
In my opinion, Unsafe.AsPointer
is more clear than a disabled CS8500 combined with the &
operator. One of the reasons why I prefer it is the disabled CS8500 applies to entire lines of code between the disable/restore, while the use of Unsafe.AsPointer
applies only to the exact call where it was needed.
It's also not clear to me how the changes proposed here would have prevented the GC hole mentioned in the original post above.
In my opinion,
Unsafe.AsPointer
is more clear than a disabled CS8500 combined with the&
operator. One of the reasons why I prefer it is the disabled CS8500 applies to entire lines of code between the disable/restore, while the use ofUnsafe.AsPointer
applies only to the exact call where it was needed.
CS8500 is a bit of a silly warning anyway imo. The issue with Unsafe.AsPointer
is that it will happily allow unsafe cases where the memory is not fixed, whereas &
will not.
e.g., consider I have this:
SomeType v = ...;
...
CallMethod(&v);
if I moved (or otherwise have a) v
anywhere that's not obviously on the stack I will get an error, whereas Unsafe.AsPointer
will happily continue to "work" even when not pinned otherwise.
It's also not clear to me how the changes proposed here would have prevented the GC hole mentioned in the original post above.
If we avoid using Unsafe.AsPointer
without a good reason that other alternatives couldn't work, then it would have been more likely to have been questioned and caught.
There are a number of unnecessary uses of
Unsafe.AsPointer
in this repository (e.g. https://github.com/dotnet/runtime/blob/eaa6f01a11eb39e1f402d8e196680fbf4aa4b054/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs#L868) - this is a extra dangerous API that can easily be used wrongly (see https://github.com/dotnet/runtime/pull/99138 as an example that slipped by a number of people). While the current remaining uses seem safe currently at a glance, we might be better off replacing a number of the unnecessary uses with other APIs, and/or adding a comment explaining why it's safe in this case (like in https://github.com/dotnet/runtime/blob/9863bf17c4abc353b867d1bcb965725384320294/src/libraries/System.Private.CoreLib/src/System/Number.BigInteger.cs#L1237), and disrecommending their use in the future without a very good reason.Most of the uses can be broken in to 2 categories:
The idea was mentioned by @tannergooding on the discord, and one of the suggested solutions was to create an internal API
bool IsOpportunisticallyAligned(ref T address, nuint aligment)
or similar for the alignment uses.I am happy to make PRs for these. @tannergooding suggested that I make one for each area separately. Just making this issue to track it and get support for the general idea from people.
/cc @stephentoub @jkotas @tannergooding