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

Remove unsafe string mutation code from repo #31821

Closed
GrabYourPitchforks opened this issue Apr 15, 2021 · 5 comments
Closed

Remove unsafe string mutation code from repo #31821

GrabYourPitchforks opened this issue Apr 15, 2021 · 5 comments
Labels
area-dataprotection Includes: DataProtection
Milestone

Comments

@GrabYourPitchforks
Copy link
Member

GrabYourPitchforks commented Apr 15, 2021

Strings in .NET are immutable, and we want to experiment with runtime features (deduplication, codegen optimizations, etc.) which rely on this fact. To accomplish this we'll need to start moving away from existing code which mutates string instances in-place. Here's a first-pass at all the places in this repo which mutate strings and recommended workarounds for them.

public static unsafe string GetLatin1StringNonNullCharacters(this ReadOnlySpan<byte> span)
{
if (span.IsEmpty)
{
return string.Empty;
}
var resultString = new string('\0', span.Length);
fixed (char* output = resultString)
fixed (byte* buffer = span)
{
// This returns false if there are any null (0 byte) characters in the string.
if (!TryGetLatin1String(buffer, output, span.Length))
{
// null characters are considered invalid
throw new InvalidOperationException();
}
}
return resultString;
}

(Similar code here and here.)

Suggested workaround (pseudocode):

fixed (byte* pBytes = bytes)
{
    return string.Create(bytes.Length, (IntPtr)pBytes, (span, ptr) =>
    {
        fixed (char* pChars = span)
        {
            WidenAndCopy((byte*)ptr, pChars, span.Length);
        }
    });
}

// Allocate a string object and write directly into it (CLR team approves of this mechanism).
string retVal = new String((char)0, checked((int)numCharsWithoutNull));
uint numBytesCopied;
fixed (char* pRetVal = retVal)
{
numBytesCopied = GetProperty(Constants.BCRYPT_ALGORITHM_NAME, pRetVal, byteLengthOfNameWithTerminatingNull);
}

Suggested workaround: Write to a stackalloced buffer, or rent a char array and write to that, then turn it into a string. Or use string.Create and avoid the intermediate rentals.

@mkArtakMSFT mkArtakMSFT added the area-dataprotection Includes: DataProtection label Apr 15, 2021
@gfoidl
Copy link
Member

gfoidl commented Apr 15, 2021

I'd like to look into this.

@BrennanConroy
Copy link
Member

While we're looking into fixing these we should keep #4720 in mind. I.e. Try to remove unsafe.

@gfoidl
Copy link
Member

gfoidl commented Apr 15, 2021

It's on my radar. Thanks.

@mkArtakMSFT mkArtakMSFT added this to the Next sprint planning milestone Apr 15, 2021
@ghost
Copy link

ghost commented Apr 15, 2021

Thanks for contacting us.
We're moving this issue to the Next sprint planning milestone for future evaluation / consideration. Because it's not immediately obvious that this is a bug in our framework, we would like to keep this around to collect more feedback, which can later help us determine the impact of it. We will re-evaluate this issue, during our next planning meeting(s).
If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues.
To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

@BrennanConroy
Copy link
Member

Done via #31850

@ghost ghost locked as resolved and limited conversation to collaborators May 22, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-dataprotection Includes: DataProtection
Projects
None yet
Development

No branches or pull requests

5 participants