Bug 28673 - Some uses of MonoPInvokeCallback lead to SIGSEGV on ARM64
Summary: Some uses of MonoPInvokeCallback lead to SIGSEGV on ARM64
Alias: None
Product: iOS
Classification: Xamarin
Component: General ()
Version: XI 8.8.0
Hardware: PC Mac OS
: Normal normal
Target Milestone: Untriaged
Assignee: Zoltan Varga
Depends on:
Reported: 2015-04-02 01:58 UTC by Brendan Zagaeski (Xamarin Team, assistant)
Modified: 2015-04-14 18:27 UTC (History)
4 users (show)

Is this bug a regression?: ---
Last known good build:

Test case (38.04 KB, application/zip)
2015-04-02 01:58 UTC, Brendan Zagaeski (Xamarin Team, assistant)
Crash log, stack trace (11.52 KB, application/zip)
2015-04-02 01:59 UTC, Brendan Zagaeski (Xamarin Team, assistant)

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description Brendan Zagaeski (Xamarin Team, assistant) 2015-04-02 01:58:34 UTC
Created attachment 10597 [details]
Test case

Some uses of MonoPInvokeCallback lead to SIGSEGV on ARM64

This could be a case of user error, but if it is, then I'm missing the key detail. Maybe it's something particular to the fact that `sqlite3_log()` has a varargs argument [1]? Or maybe I'm missing something about converting `ErrorLogCallback`? (The native type of the `ErrorLogCallback` function is supposed to be `void(*)(void*,int,const char*)` [2].)

> [1] https://www.sqlite.org/c3ref/log.html

> [2] See "SQLITE_CONFIG_LOG" on https://www.sqlite.org/c3ref/c_config_covering_index_scan.html

## Steps to reproduce

Build and run the attached test case on a 64-bit physical iOS device.

## About the test case

The essential code from the test case is:

> public delegate void ErrorLogCallback (IntPtr pArg, nint iErrCode, string zMsg);
> [DllImport("sqlite3", EntryPoint = "sqlite3_config", CallingConvention=CallingConvention.Cdecl)]
> public static extern Result Config (ConfigOption option, ErrorLogCallback callback, IntPtr pArg);
> [DllImport("sqlite3", EntryPoint = "sqlite3_log", CallingConvention=CallingConvention.Cdecl)]
> public static extern void Log (nint iErroCode, string zFormat, string zMsg);
> [MonoPInvokeCallback (typeof (ErrorLogCallback))]
> public static void SQLite3ErrorCallback (IntPtr pArg, nint iErrCode, string zMsg)
> {
>     Console.WriteLine("Called the callback! {0}", zMsg);
> }
> public override void ViewDidLoad()
> {
>     base.ViewDidLoad();
>     Config(ConfigOption.Log, SQLite3ErrorCallback, IntPtr.Zero);
>     Log(1, "%s", "Hello world.");
> }

The test case includes a corresponding Xcode Objective-C app that does not crash.

## Result

The app crashes after the call to `Log()`, apparently when the native `sqlite3_log()` method attempts to call back to `SQLite3ErrorCallback()`.

To make a completely wild (and uneducated) guess, perhaps the address on the top of the stack is incorrect (that is, maybe `sqlite3_log()` isn't using the correct address for `SQLite3ErrorCallback()`)?

### Excerpt from the symbolicated crash log

> Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
> Exception Subtype: KERN_PROTECTION_FAILURE at 0x000000016fd1d6e0
> Triggered by Thread:  0
> Thread 0 name:  Dispatch queue: com.apple.main-thread
> Thread 0 Crashed:
> 0   ???                           	0x000000016fdad6d0 0 + 6171580112
> 1   UnifiedSingleViewIphone1      	0x0000000100055560 wrapper_managed_to_native_UnifiedSingleViewIphone1_UnifiedSingleViewIphone1ViewController_Log_System_nint_string_string + 272
> 2   UnifiedSingleViewIphone1      	0x0000000100054914 UnifiedSingleViewIphone1_UnifiedSingleViewIphone1ViewController_ViewDidLoad + 516

### Excerpt from the `monobt` backtrace after catching the crash in Xcode/lldb

> frame #0: 0x000000016fdad6d0
> frame #1: 0x0000000197885a14 libsqlite3.dylib`sqlite3_log + 412
> frame #2: 0x0000000100055564 UnifiedSingleViewIphone1`wrapper_managed_to_native_UnifiedSingleViewIphone1_UnifiedSingleViewIphone1ViewController_Log_System_nint_string_string + 276
> frame #3: 0x0000000100054918 UnifiedSingleViewIphone1`UnifiedSingleViewIphone1_UnifiedSingleViewIphone1ViewController_ViewDidLoad + 520

## Additional notes

- Switching from `nint` back to `int` does not affect the outcome.

- Changing the `MtouchArch` to target only 32-bit architectures (e.g., just ARMv7) does prevent the problem on 64-bit devices (tested on iPad Mini 2, iOS 8.0).

- The app runs without error when built for the x86_64 architecture and run an x86_64 simulator.

## Version information

### OS X 10.9.5, MacBook Air

Mono 3.12.1 (detached/0849ec7)

Xcode 6.1.1 (6611), Build 6A2008a

Hash: 981acb3
Build date: 2015-03-25 14:21:05-0400

### Also tested quickly

Hash: 4dac6bf
Branch: master
Build date: 2015-04-01 21:55:11-0400
Comment 1 Brendan Zagaeski (Xamarin Team, assistant) 2015-04-02 01:59:15 UTC
Created attachment 10599 [details]
Crash log, stack trace
Comment 2 Sebastien Pouliot 2015-04-02 10:02:41 UTC
> `sqlite3_log()` has a varargs argument

That could be.

@Zoltan does Mono's callback system handle those on ARM64 ?
Comment 3 Zoltan Varga 2015-04-02 12:44:39 UTC
So on ios, vararg functions are called entirely differently than normal functions. We can fix it, but currently there is no way to mark a pinvoke declaration as vararg, since the CallingConvention enum doesn't have a Vararg member, probably because on all other platforms, vararg and non-vararg functions are called the same way.
Comment 4 Zoltan Varga 2015-04-02 12:56:57 UTC
Another problem is that there is no way to mark where the optional arguments begin. The only solution I see is implementing support for vararg+pinvoke, either using the 'params' c# facility, or using __arglist. Both require a significant amount of work.
Comment 5 Rolf Bjarne Kvinge [MSFT] 2015-04-07 12:41:51 UTC
The problem in this case isn't the method with the MonoPInvokeCallback (since it's not a varargs member), but that the P/Invoke methods are varargs, which ends up shifting arguments around and then native code uses a random value on the stack for the callback.

It's possible to work around the special arm64 varargs calling convention for P/Invokes by special-casing it like this: https://gist.github.com/rolfbjarne/cc147285de8e9734aec2 (basically make sure there are 8 arguments before the optional ones).
Comment 6 Brendan Zagaeski (Xamarin Team, assistant) 2015-04-14 18:27:01 UTC
Cool. Many thanks for the workaround!