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

Extension of IL verification rules to address ref returns #17

Merged
merged 2 commits into from
Jan 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@ Design proposals will start as issues and will be moved to official design docum

Anyone can contribute. The following contributions are expected to be the most common:

- Create an issue requesting a new feature.
- Create an issue requesting a design for an existing feature where a behavior is problematic or not well understood.
- Comment on an issue.
- Create a pull request for a doc in .\accepted for a design for a feature.
- Create a pull request for a doc in .\accepted an alternate design for a
- Create a pull request for a doc in .\proposed for a design for a feature.
- Create a pull request for a doc in .\proposed an alternate design for a
feature.
- Once broad agreement is reached, PR proposal doc merged into accepted folder.

Expand Down
75 changes: 75 additions & 0 deletions proposed/verifiable-ref-returns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Ref returns in C# and IL verification. #

The goal of this document is to specify additional IL verification rules that would allow C# code that utilizes ref returns be formally verifiable.

Even in a post-CAS world verifiability is an important property. It is a subset of type-safe code that can be validated by a tool. As such it is often a soundness prerequisite for other kind of analysis. Besides, verifier is a great testing tool that keeps compilers honest about their type-safe claims.

While we believe that the current rules of the language result in a type-safe code, the formal verification rules for the emitted IL and PEVerify (or any tool of similar purpose) have not yet been updated to accept such code.

## C# rules for reference: ##

1) refs to variables on the heap are safe to return
2) ref parameters are safe to return
3) out parameters are safe to return (but must be definitely assigned, as is already the case today)
4) instance struct fields are safe to return as long as the receiver is safe to return
5) �this� is not safe to return from struct members
6) a ref, returned from another method is safe to return if all refs/outs passed to that method as formal parameters were safe to return.
*Specifically it is irrelevant if receiver is safe to return, regardless whether receiver is a struct, class or typed as a generic type parameter.*

NOTE: C# rules operate with high level language entities such as scopes, expressions, local variables, etc., that do not exist at the level of IL or do not map one-to-one.
The following is the attempt to introduce/adjust verification rules to allow validation of the safety of the ref returns at IL level.

# Verification of ref returns. #

## Returnable managed pointer. ##

CLI needs to add a notion of another transient type - returnable managed pointer.
*(need to adjust �III.1.8.1.1 Verification algorithm� and in �I.8.7 Assignment compatibility� accordingly)*

NOTE: there are similarities with already existing concept of controlled mutability managed references.

Returnable managed pointer is a transient type used for verification purposes. When certain conditions are met, managed pointers can be classified as returnable. Unlike ordinary managed pointer types, returnable managed pointers can be used as operands of `RET` instruction in verifiable code.

Returnable managed pointer may be a result of one of the following:
- `LDSFLDA`, `LDELEMA`, `UNBOX` always returnable (see: C# rule #1)
- `LDFLDA` when the receiver is a reference type (see: C# rule #1).
- `LDFLDA` when the receiver is a returnable ref (see: C# rule #4)
- `LDARG` of a byref parameter (see: C# rule #2, #3),
except for arg0 in a struct method (see: C# rule #5).
- `CALL`, `CALLVIRT` when all reference arguments are returnable. (see: C# rule 6)
Copy link
Member

Choose a reason for hiding this comment

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

Should this also apply to CALLI?

Copy link
Member Author

Choose a reason for hiding this comment

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

The rule could include CALLI for completeness. However CALLI cannot be present in verifiable code so generalization would not make much difference.

Copy link
Member

Choose a reason for hiding this comment

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

However CALLI cannot be present in verifiable code

Why not? ECMA-335 defines verification rules for when CALLI is verifiable. It's not like JMP (whose verification rules are simply "The jmp instruction is never verifiable").

Copy link
Member Author

Choose a reason for hiding this comment

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

Is it? Perhaps I think of it as unverifiable because it invariably involves pointers.
Anyways, no problem with including it.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think, I should mention varargs/arglist case as well.

C# compiler does consider them for the purpose of "any ref argument can potentially come back as a ref return"

note: arg0 in this-calls are not considered here since it cannot be returned by reference.
- `LDLOC` of a byref local when the slot marked �returnable� (IL specific rule)

All other ways of obtaining a managed reference (Ex: `LDARGA`, `LDLOCA`, `LOCALLOC`, `REFANYVAL`�) do not result in returnable references.

## Merging stack states ##
*(add to III.1.8.1.3 Merging stack states)*

Merging a returnable managed pointer with an ordinary (that is, non-returnable) managed pointer to the same type results in a non-returnable managed pointer to that type.

## Returnable local slots ##
CLI needs to add a notion of a returnable local slots.
- a local byref slot may be marked as returnable.
- `STLOC` into returnable slots is verifiable only when the value is a returnable managed pointer.
- `LDLOC` from a returnable slot produces a returnable managed pointer.

The rationale for introducing returnable locals is to allow for a single pass verification analysis.
While, in theory, it may be possible that the �returnable� property could be inferred via some form of fixed point flow analysis, it would make verification substantially more complex and expensive and would not retain the single-pass property.

## Metadata encoding of returnable local slots ##

Local slots can be marked as returnable by applying `modopt[System.Runtime.CompilerServices.IsReturnableAttribute]` in the local signature.

In particular:
- The identity of the `IsReturnableAttribut`e type is unimportant. In fact we expect that it would be embedded by the compiler in the containing assembly.
- Applying `IsReturnableAttribute` to byval local slots is reserved for the future use and in scope of this proposal results in verification error.

---
NOTE: while JIT is free to ignore the differences between returnable and ordinary byref locals. However it may use that extra information as an input/hints when performing optimizations.

---
NOTE: Why `modopt`?

While in principle, a new signature constraint, similar to `pinned`, could be a more elegant solution, experiments have shown that introducing a new constraint is breaking to existing JITs.

On the other hand a `modopt` applied to a local is completely ignored by JITs, irrelevant to external consumers such as compilers and transparently supported by IL-level introspection tools such as decompilers. This makes `modopt` a convenient mechanism to implement tagging of local slots.