From b40b9b2c5c6ae5b01fecacf8266b0a145338043f Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 2 Jan 2026 17:45:41 +0100 Subject: [PATCH 1/5] Add III.1.7 "Managed pointers exposing parameters outside of the method scope" to Ecma-335-Augments.md --- docs/design/specs/Ecma-335-Augments.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/design/specs/Ecma-335-Augments.md b/docs/design/specs/Ecma-335-Augments.md index a26490f87b71c2..2316a5c7bbb723 100644 --- a/docs/design/specs/Ecma-335-Augments.md +++ b/docs/design/specs/Ecma-335-Augments.md @@ -1030,6 +1030,13 @@ Changes to signatures: - Managed pointers which point at the address just past the end of an object, or the address where an element just past the end of an array would be stored, are permitted but not dereferenceable. - Null managed pointers are permitted to be dereferenced resulting in a `NullReferenceException`. +### III.1.7.7 +Add a new paragraph "III.1.7.7 Managed pointers exposing parameters outside of the method scope" under section "III.1.7 Restrictions on CIL code sequences" +Byrefs derived from method parameters can escape from a function only in the following ways: +- By explicit return of a byref or byref-like type +- By writing through a byref to a byref-like type or byref-like type containing multiple levels of byref fields. For an example: `Span p` parameter cannot expose anything, while `ref Span p` can. +The behavior is undefined if a any other form of escape is attempted. + ## ByRefLike types in generics ByRefLike types, defined in C# with the `ref struct` syntax, represent types that cannot escape to the managed heap and must remain on the stack. It is possible for these types to be used as generic parameters, but in order to improve utility certain affordances are required. From a998b239e403a4fab844ab97c15cbb34c67c3521 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Fri, 2 Jan 2026 18:02:19 +0100 Subject: [PATCH 2/5] Update docs/design/specs/Ecma-335-Augments.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/design/specs/Ecma-335-Augments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/specs/Ecma-335-Augments.md b/docs/design/specs/Ecma-335-Augments.md index 2316a5c7bbb723..b4a02d79824504 100644 --- a/docs/design/specs/Ecma-335-Augments.md +++ b/docs/design/specs/Ecma-335-Augments.md @@ -1034,7 +1034,7 @@ Changes to signatures: Add a new paragraph "III.1.7.7 Managed pointers exposing parameters outside of the method scope" under section "III.1.7 Restrictions on CIL code sequences" Byrefs derived from method parameters can escape from a function only in the following ways: - By explicit return of a byref or byref-like type -- By writing through a byref to a byref-like type or byref-like type containing multiple levels of byref fields. For an example: `Span p` parameter cannot expose anything, while `ref Span p` can. +- By writing through a byref to a byref-like type or byref-like type containing multiple levels of byref fields. For example: `Span p` parameter cannot expose anything, while `ref Span p` can. The behavior is undefined if a any other form of escape is attempted. ## ByRefLike types in generics From cdc82d1aa6aace1365f578dc925fab1611d97daf Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Fri, 2 Jan 2026 18:02:30 +0100 Subject: [PATCH 3/5] Update docs/design/specs/Ecma-335-Augments.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/design/specs/Ecma-335-Augments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/specs/Ecma-335-Augments.md b/docs/design/specs/Ecma-335-Augments.md index b4a02d79824504..d5f13e4f29ab8b 100644 --- a/docs/design/specs/Ecma-335-Augments.md +++ b/docs/design/specs/Ecma-335-Augments.md @@ -1035,7 +1035,7 @@ Add a new paragraph "III.1.7.7 Managed pointers exposing parameters outside of t Byrefs derived from method parameters can escape from a function only in the following ways: - By explicit return of a byref or byref-like type - By writing through a byref to a byref-like type or byref-like type containing multiple levels of byref fields. For example: `Span p` parameter cannot expose anything, while `ref Span p` can. -The behavior is undefined if a any other form of escape is attempted. +The behavior is undefined if any other form of escape is attempted. ## ByRefLike types in generics From bf655e28ad7def640676380233ba3f3cd2ef2ab8 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Fri, 2 Jan 2026 18:03:20 +0100 Subject: [PATCH 4/5] Update Ecma-335-Augments.md --- docs/design/specs/Ecma-335-Augments.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/design/specs/Ecma-335-Augments.md b/docs/design/specs/Ecma-335-Augments.md index d5f13e4f29ab8b..b92f3c9c407ec2 100644 --- a/docs/design/specs/Ecma-335-Augments.md +++ b/docs/design/specs/Ecma-335-Augments.md @@ -1036,7 +1036,7 @@ Byrefs derived from method parameters can escape from a function only in the fol - By explicit return of a byref or byref-like type - By writing through a byref to a byref-like type or byref-like type containing multiple levels of byref fields. For example: `Span p` parameter cannot expose anything, while `ref Span p` can. The behavior is undefined if any other form of escape is attempted. - + ## ByRefLike types in generics ByRefLike types, defined in C# with the `ref struct` syntax, represent types that cannot escape to the managed heap and must remain on the stack. It is possible for these types to be used as generic parameters, but in order to improve utility certain affordances are required. @@ -1201,4 +1201,4 @@ In section II.23.1.15, the following row is added to the table: ## Implict argument coercion rules -Implicit argument coercion as defined in section III.1.6 does not match with existing practice in CLR runtimes. Notably, implicit argument coercion of an `int32` on the IL evaluation stack to a `native unsigned int` is a sign extending operation, not a zero-extending operation. \ No newline at end of file +Implicit argument coercion as defined in section III.1.6 does not match with existing practice in CLR runtimes. Notably, implicit argument coercion of an `int32` on the IL evaluation stack to a `native unsigned int` is a sign extending operation, not a zero-extending operation. From b048e5ab1c4ca3fc82243ff50f771303e42ae0d9 Mon Sep 17 00:00:00 2001 From: egorbot Date: Fri, 13 Mar 2026 05:27:21 +0100 Subject: [PATCH 5/5] Clarify managed pointer escape wording Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/design/specs/Ecma-335-Augments.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/design/specs/Ecma-335-Augments.md b/docs/design/specs/Ecma-335-Augments.md index b92f3c9c407ec2..8d00cbe1338940 100644 --- a/docs/design/specs/Ecma-335-Augments.md +++ b/docs/design/specs/Ecma-335-Augments.md @@ -1032,10 +1032,13 @@ Changes to signatures: ### III.1.7.7 Add a new paragraph "III.1.7.7 Managed pointers exposing parameters outside of the method scope" under section "III.1.7 Restrictions on CIL code sequences" -Byrefs derived from method parameters can escape from a function only in the following ways: +Managed pointers obtained from method parameters, and byref-like values that contain them, can be made observable outside of a function only through escape paths present in the function signature: - By explicit return of a byref or byref-like type -- By writing through a byref to a byref-like type or byref-like type containing multiple levels of byref fields. For example: `Span p` parameter cannot expose anything, while `ref Span p` can. -The behavior is undefined if any other form of escape is attempted. +- By writing to storage exposed by a byref parameter, including byref-like types containing multiple levels of byref fields + +The same rule applies to unsafe code. Copying such a managed pointer or byref-like value through unmanaged or other raw storage is permitted, but it is undefined behavior to copy it into storage that makes it observable outside of the function through an escape path not present in the signature. Temporary copies in storage that is not observable outside of the function are permitted. + +For example: `void M(Span p)` cannot expose `p` outside of `M`, while `void M(Span p, ref Span q)` can copy `p` into `q`. ## ByRefLike types in generics