Skip to content

SEHException with new Exception behavior in .NET 9 #111242

Closed
@kevin-montrose

Description

@kevin-montrose

Description

Starting in .NET 9, pinvoking Lua's luaL_error can raise a SEHException.

I believe this is due to luaL_error being a relatively thin wrapper over longjmp.

I narrowed this down to the newly defaulted on improved exception handling in .NET 9.

Note that while I use KeraLua in my repro, the same thing happens if you pinvoke Lua directly - KeraLua is just a convenient way to package up a pre-built Lua 5.4 binary, and eliminate some of my own pinvoke definition from the repro.

Reproduction Steps

csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>net8.0;net9.0</TargetFrameworks>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="KeraLua" Version="1.4.1" />
  </ItemGroup>

</Project>

Program.cs

using KeraLua;

namespace LongJumpRepro
{
    public class Program
    {
        private const string ErrorMsg = "!!RAISED!!";

        private static Lua? lua;

        public static void Main(string[] args)
        {
            // This works as expected in .NET 8
            // It raises a SEHException in .NET 9
            // The exception in .NET 9 goes away if you set environment variable DOTNET_LegacyExceptionHandling=1

            Console.WriteLine($".NET version: {Environment.Version}");
            Console.WriteLine($"DOTNET_LegacyExceptionHandling: {Environment.GetEnvironmentVariable("DOTNET_LegacyExceptionHandling")}");

            try
            {
                lua = new Lua();
                lua.PushCFunction(RaiseLuaError);
                var status = lua.PCall(0, 0, 0);
                Console.WriteLine($"Status (Expected {LuaStatus.ErrRun}): {status}");

                var errMsg = lua.CheckString(1);
                Console.WriteLine($"Error Message (Expected '{ErrorMsg}'): {ErrorMsg}");
            }
            finally
            {
                lua?.Dispose();
            }
        }

        private static int RaiseLuaError(nint luaState)
        => lua!.Error(ErrorMsg);
    }
}

Expected behavior

Expected behavior is either the .NET 8:
Image
or .NET 9 with DOTNET_LegacyExceptionHandling=1 behavior:
Image

Where luaL_error works when pinvoked.

Actual behavior

A SEHException is raised:
Image

Regression?

Yes, this worked in earlier versions of .NET. I have confirmed it worked in .NET 8, and this was reported when first running the code on .NET 9.

Known Workarounds

Setting DOTNET_LegacyExceptionHandling=1 fixes the issue in .NET 9.

Discussion on the PR that introduced it suggests it is temporary however, if it is removed in a later .NET release we will no longer have a workaround.

Configuration

  • .NET 8 and .NET 9
  • Windows 11
  • x64
  • I have not tested on other configurations
  • I am not using Blazor

Other information

While I admit longjmp is weird, embedding Lua is pretty popular.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions