dotnet / fsharp

The F# compiler, F# core library, F# language service, and F# tooling integration for Visual Studio
https://dotnet.microsoft.com/languages/fsharp
MIT License
3.94k stars 788 forks source link

.Net Core FSI causes security exception when attempting to executing a PInvoke call from fsx scripts #6544

Closed patrickallensimpson closed 5 years ago

patrickallensimpson commented 5 years ago

Don Syme asked me to move this bug from fsharp/fsharp issue 886 to here. I describe a failure mode where F# interactive running under .Net Core won't generate correct PInvoke calls in your scripts. It was determined by @jkotas here and @KevinRansom here that the issue was related to originally missing functionality in .Net Core's Reflection.Emit.TypeBuilder that is now present in .Net Core 3.0.

Considering that we now have F# 4.6 FSI as a preview dotnet command dotnet fsi inside the .Net Core 3.0 preview 3 this needs to get fixed otherwise any existing scripts using PInvoke will not work reporting a rather esoteric error message. This is still an issue under 3.0.100-preview3-010431 running Microsoft (R) F# Interactive version 10.4.0 for F# 4.6

Here is the original issue:

When running the .Net Core version of the F# Interactive Service we get a security exception on any invocation of a PInvoke call that was generated in the script itself.

  1. Start .Net Core version of fsi.exe

    dotnet 'C:\Program Files\dotnet\sdk\3.0.100-preview-009812\FSharp\fsi.exe'
  2. Enter the following script:

open System.Runtime.InteropServices
[<DllImport("kernel32")>]
extern int GetLastError()

GetLastError()
  1. You'll see the following exception:
System.Security.SecurityException: ECall methods must be packaged into a system module.
   at <StartupCode$FSI_0012>.$FSI_0012.main@()
Stopped due to error

On .Net Framework fsi this will execute and return the last error this is just the simplest test case I could think of.

I investigated and this is due to a check in ecall.cpp:346 inside the coreclr project.


    if (!pMD->GetModule()->IsSystem())
        COMPlusThrow(kSecurityException, BFA_ECALLS_MUST_BE_IN_SYS_MOD);

They perform a check on the incoming module to see if it is a system module (My guess is this means it's loaded from disk?) In any case if it is not then they throw this error. This means any scripts that were using PInvoke to call win32 services or other C api's CAN NOT work on the CoreCLR fsi version.

I've confirmed this is the case with .Net Core 2.0, 2.1, 2.2 and 3.0 preview at the exact same line number.

It there something that can be done to let the CoreCLR system know that the dynamically generated FSI assemblies are ok as far as this check is concerned?

Thanks, -Patrick Simpson

DotNetFSISecurityFailureTest.zip

patrickallensimpson commented 5 years ago

I'd fix this myself but I need guidance on the appropriate way we should special case this for .Net Core 3 builds in the project. My understanding of the issue is that when compiling under .Net Core 3 we simply need to remove the #if and keep the containing code that surrounds lines here: https://github.com/Microsoft/visualfsharp/blob/5cf2d974241efb1bfa525cb636d392960ce1f0b7/src/absil/ilreflect.fs#L1515-L1541

Thanks, -Patrick

matthid commented 5 years ago

@patrickallensimpson It looks like @KevinRansom is already working on it in https://github.com/Microsoft/visualfsharp/pull/6542

patrickallensimpson commented 5 years ago

I just commented on that pull request, I'm very happy about this new fix!

-Patrick