Skip to content

docs: clarify String.GetPinnableReference() returns a read-only interior pointer#130237

Closed
AaronRobinsonMSFT with Copilot wants to merge 2 commits into
mainfrom
copilot/fix-wchar-t-buffer-marshaling
Closed

docs: clarify String.GetPinnableReference() returns a read-only interior pointer#130237
AaronRobinsonMSFT with Copilot wants to merge 2 commits into
mainfrom
copilot/fix-wchar-t-buffer-marshaling

Conversation

Copilot AI commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

String.GetPinnableReference() (and the equivalent C++/CLI PtrToStringChars) returns an interior pointer into an immutable managed string. The existing doc comment didn't communicate this constraint, leaving the door open for dangerous patterns like casting away const in C++/CLI and writing through the pointer — which silently corrupts the CLR heap.

Change: Expanded the XML doc on String.GetPinnableReference() to explicitly state:

  • The reference is ref readonly — reads only; writes are undefined behavior
  • In C++/CLI, PtrToStringChars surfaces this as const wchar_t* for exactly this reason
  • Casting away const and writing through the pointer will silently corrupt the managed heap, with crashes manifesting at unrelated call sites

…aint

Closes #130225

The XML documentation on `String.GetPinnableReference()` has been updated
to explicitly state that the returned `ref readonly char` interior pointer
is read-only. Writing to the referenced memory (for example, by casting away
`const` in C++/CLI when using `PtrToStringChars`) is not permitted and will
silently corrupt the CLR heap.

Co-authored-by: AaronRobinsonMSFT <30635565+AaronRobinsonMSFT@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot July 6, 2026 06:17
Copilot AI changed the title [WIP] Fix C++/CLI marshaling of wchar_t* buffer to prevent heap corruption docs: clarify String.GetPinnableReference() returns a read-only interior pointer Jul 6, 2026
Copilot AI requested a review from AaronRobinsonMSFT July 6, 2026 06:18
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-runtime
See info in area-owners.md if you want to be subscribed.

/// <para>
/// When using this method (or the equivalent C++/CLI <c>PtrToStringChars</c> function) to
/// obtain a pinned interior pointer to the string's character data, the resulting pointer
/// must be treated as read-only. In C++/CLI, <c>PtrToStringChars</c> exposes the pointer

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think we want to be talking about C++/CLI specific methods in the documentation for System.String.

The discussion of C++/CLI PtrToStringChars should be in C++/CLI documentation.

Comment on lines +523 to +539
/// <remarks>
/// <para>
/// The <see cref="string"/> type is immutable: the value of a <see cref="string"/> object
/// cannot be changed after the object is created. The reference returned by this method is
/// <c>ref readonly</c> and must only be used for reading characters; writing to
/// the referenced memory is not permitted and results in undefined behavior, including
/// potential corruption of the managed heap.
/// </para>
/// <para>
/// When using this method (or the equivalent C++/CLI <c>PtrToStringChars</c> function) to
/// obtain a pinned interior pointer to the string's character data, the resulting pointer
/// must be treated as read-only. In C++/CLI, <c>PtrToStringChars</c> exposes the pointer
/// as <c>const wchar_t*</c> to reflect this constraint. Casting away <c>const</c>
/// and writing through the pointer will silently corrupt the CLR heap and may cause
/// crashes or incorrect behavior at an unrelated location later in program execution.
/// </para>
/// </remarks>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method already returns a ref readonly char; why do we need to get into this much detail that we should not mutate the returned memory?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree - this method alone is the least of the problem since it returns ref readonly.

The general issue is covered in https://learn.microsoft.com/en-us/dotnet/standard/unsafe-code/best-practices#17-string-mutations . There are many ways to get the pointer to string characters using unsafe code, it applies to all of them.

/// Returns a read-only reference to the first element of the String. If the string is null, an access will throw a NullReferenceException.
/// </summary>
/// <remarks>
/// <para>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The source of truth for official docs in dotnet-api-docs repo has number of other remarks: https://learn.microsoft.com/en-us/dotnet/api/system.string.getpinnablereference?view=net-10.0

It may be better to do the changes there.

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

This isn't where I was expecting copilot to do the work. There is already a doc page for this and that should be updated, nothing in the runtime.

@AaronRobinsonMSFT AaronRobinsonMSFT deleted the copilot/fix-wchar-t-buffer-marshaling branch July 6, 2026 16:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

C++/CLI marshaling of wchar_t* buffer causes silent CLR heap corruption

4 participants