Bug 21261 - NullReferenceException after call native method with arguments(struct) passed by ref (ex Ping.cs)
Summary: NullReferenceException after call native method with arguments(struct) passed...
Status: RESOLVED FIXED
Alias: None
Product: Runtime
Classification: Mono
Component: Interop ()
Version: 3.4.0
Hardware: PC Linux
: --- normal
Target Milestone: ---
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2014-07-11 09:51 UTC by Nikolay Filchenko
Modified: 2017-06-29 21:49 UTC (History)
6 users (show)

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

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 GitHub or Developer Community 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:
Status:
RESOLVED FIXED

Description Nikolay Filchenko 2014-07-11 09:51:36 UTC
Call to native method (capget from libc.so) works, but any following instruction throws NullReferenceException.
Firs bug found in System.Net.NetworkInformation.Ping.CheckLinuxCapabilities. 

Minimum test case is just body of this method. After replacing refs by IntPtr to arrays it works.

This works:

try {
  var header = new UInt32[2];
  header[0] = linux_cap_version;
  header[1] = 0;
  var data = new UInt32[3];
  int ret = -1;
  try {
    fixed(uint * hptr = header)
    {
      fixed (uint * dptr = data)
      {
        ret = capget (new UIntPtr(hptr), new UIntPtr(dptr) );
      }
    }
  } 
  catch (Exception) 
  {
  }

if (ret == -1)
  return;
canSendPrivileged = (data[0] & (1 << 13)) != 0;
} catch (Exception ) {
  canSendPrivileged = false;
}

This is not work:
try {
  cap_user_header_t header = new cap_user_header_t ();
  cap_user_data_t data = new cap_user_data_t ();
  header.version = linux_cap_version;
  int ret = -1;
  try {
    ret = capget (ref header, ref data);
  } catch (Exception) { //After this line debugger jump to catch
  }
  if (ret == -1)
    return;
  canSendPrivileged = (data.effective & (1 << 13)) != 0;
} catch {
  canSendPrivileged = false; //Debugger stops with NullReference here
}
Comment 1 Zoltan Varga 2014-07-11 10:25:34 UTC
Does this fail only under the debugger, or when running normally too ?
Comment 2 Nikolay Filchenko 2014-07-11 10:31:08 UTC
Debug build with debugger: Fail
Debug build without debugger: Fail
Release build with debugger: Fail
Release build without debugger: Works
Comment 3 Nikolay Filchenko 2014-07-11 10:36:24 UTC
It was about System.Net.NetworkInformation.Ping, mono was not rebuilt, only application.
Comment 4 Zoltan Varga 2014-07-11 14:54:06 UTC
Cannot reproduce this with the following testcase:
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
using System;
using System.Runtime.InteropServices;

public class Tests
{
		[StructLayout(LayoutKind.Sequential)]
		struct cap_user_header_t
		{
			public UInt32 version;
			public Int32 pid;
		};

		[StructLayout(LayoutKind.Sequential)]
		struct cap_user_data_t
		{
			public UInt32 effective;
			public UInt32 permitted;
			public UInt32 inheritable;
		}

		const UInt32 linux_cap_version = 0x20071026;

		[DllImport ("libc", EntryPoint="capget")]
		static extern int capget (ref cap_user_header_t header, ref cap_user_data_t data);

	public static void Main (String[] args) {
		cap_user_header_t header = new cap_user_header_t ();
		cap_user_data_t data = new cap_user_data_t ();

		header.version = linux_cap_version;

		int ret = -1;

		for (int i = 0; i < 10; ++i) {
		try {
			ret = capget (ref header, ref data);
			Console.WriteLine (ret);
		} catch (Exception) {
		}
		}
	}
}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Comment 5 Nikolay Filchenko 2014-07-17 03:52:39 UTC
My OS is 3.10.25-gentoo
gcc version 4.7.4 (Gentoo 4.7.4 p1.0, pie-0.5.5) 
glibc version 2.17
Comment 6 Rodrigo Kumpera 2014-07-21 18:03:42 UTC
Hey Nikolay,

Does Zoltan's test case shows the problem for you?

If not, provide a full program that shows the problematic behavior.
Comment 7 eb1 2017-06-29 16:37:07 UTC
I get the same crash when I run in the debugger (on a 64-bit machine). The reason seems to be that we request 64-bit capabilities by specifying _LINUX_CAPABILITY_VERSION_2 (0x20071026). The capabilities are set in two data elements, but we allocate only one.

Requesting 32-bit capabilities with _LINUX_CAPABILITY_VERSION_1 fixes the problem for me.

cf http://man7.org/linux/man-pages/man2/capget.2.html
Comment 8 eb1 2017-06-29 16:37:59 UTC
Oops, I meant to say: I get the crash when I run Zoltan's test case in the debugger.
Comment 9 eb1 2017-06-29 16:55:23 UTC
Suggested fix in PR #5150.
Comment 10 Alexander Köplinger [MSFT] 2017-06-29 21:49:22 UTC
PR was merged, thanks!