P/Invoke Ioctl System Call

P/Invoke ioctl system call

Example usage:

Capability capability;

if (UnsafeNativeMethods.Ioctl(handle, request, ref capability) == -1)
{
throw new UnixIOException();
}

Capability:

[StructLayout(LayoutKind.Sequential, Size = 104)]
internal struct Capability
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string Driver;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string Device;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string BusInfo;

public uint Version;

public CapabilityFlags Capabilities;
}

UnsafeNativeMethods:

internal static class UnsafeNativeMethods
{
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
[DllImport("libc", EntryPoint = "close", SetLastError = true)]
internal static extern int Close(IntPtr handle);

[DllImport("libc", EntryPoint = "ioctl", SetLastError = true)]
internal static extern int Ioctl(SafeUnixHandle handle, uint request, ref Capability capability);

[DllImport("libc", EntryPoint = "open", SetLastError = true)]
internal static extern SafeUnixHandle Open(string path, uint flag, int mode);

internal static string Strerror(int error)
{
try
{
var buffer = new StringBuilder(256);
var result = Strerror(error, buffer, (ulong)buffer.Capacity);
return (result != -1) ? buffer.ToString() : null;
}
catch (EntryPointNotFoundException)
{
return null;
}
}

[DllImport("MonoPosixHelper", EntryPoint = "Mono_Posix_Syscall_strerror_r", SetLastError = true)]
private static extern int Strerror(int error, [Out] StringBuilder buffer, ulong length);
}

SafeUnixHandle:

[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
internal sealed class SafeUnixHandle : SafeHandle
{
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
private SafeUnixHandle()
: base(new IntPtr(-1), true)
{
}

public override bool IsInvalid
{
get { return this.handle == new IntPtr(-1); }
}

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
protected override bool ReleaseHandle()
{
return UnsafeNativeMethods.Close(this.handle) != -1;
}
}

UnixIOException:

[Serializable]
public class UnixIOException : ExternalException
{
private readonly int nativeErrorCode;

[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
public UnixIOException()
: this(Marshal.GetLastWin32Error())
{
}

[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
public UnixIOException(int error)
: this(error, GetErrorMessage(error))
{
}

[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
public UnixIOException(string message)
: this(Marshal.GetLastWin32Error(), message)
{
}

public UnixIOException(int error, string message)
: base(message)
{
this.nativeErrorCode = error;
}

public UnixIOException(string message, Exception innerException)
: base(message, innerException)
{
}

protected UnixIOException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
this.nativeErrorCode = info.GetInt32("NativeErrorCode");
}

public int NativeErrorCode
{
get { return this.nativeErrorCode; }
}

public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (info == null)
{
throw new ArgumentNullException("info");
}

info.AddValue("NativeErrorCode", this.nativeErrorCode);
base.GetObjectData(info, context);
}

private static string GetErrorMessage(int error)
{
var errorDescription = UnsafeNativeMethods.Strerror(error);
return errorDescription ?? string.Format("Unknown error (0x{0:x})", error);
}
}

Using Linux ioctl with Mono

Mono does not contain a wrapper for ioctl in Mono.Unix, because ioctl call parameters vary greatly and such a wrapper would be almost useless. You should declare a DllImport for each ioctl you need.

You probably don't need a helper library written in C, however, you may need it during development to extract actual values hidden behind different C preprocessor macros. For example, to expand C header:

#define FE_GET_INFO                _IOR('o', 61, struct dvb_frontend_info)

compile and execute this helper:

#include <linux/dvb/frontend.h>
#include <stdio.h>

int main()
{
printf("const int FE_GET_INFO = %d;\n", FE_GET_INFO);
return 0;
}

A short mono mailing list discussion on the topic.

C# - Possible to use IOCTL

The C++ is riddled with mistakes, not sure if I got it right. The best thing to do is to declare DeviceIoControl() with altered argument types so that it is easy to call. You also have to P/Invoke CreateFile because FileStream cannot open devices. It ought to look similar to this:

using System;
using System.IO;
using System.ComponentModel;
using System.Runtime.InteropServices;

class Program {
static void Main(string[] args) {
IntPtr hdl = CreateFile("\\\\.\\ADVSYS", FileAccess.ReadWrite,
FileShare.None, IntPtr.Zero, FileMode.Open,
FileOptions.None, IntPtr.Zero);
if (hdl == (IntPtr)(-1)) throw new Win32Exception();
try {
byte drawer = 1;
bool ok = DeviceIoControl(hdl, CTLCODE, ref drawer, 1, IntPtr.Zero,
0, IntPtr.Zero, IntPtr.Zero);
if (!ok) throw new Win32Exception();
}
finally {
CloseHandle(hdl);
}
}
// P/Invoke:
private const uint CTLCODE = 0xdaf52480;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CreateFile(string filename, FileAccess access,
FileShare sharing, IntPtr SecurityAttributes, FileMode mode,
FileOptions options, IntPtr template
);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool DeviceIoControl(IntPtr device, uint ctlcode,
ref byte inbuffer, int inbuffersize,
IntPtr outbuffer, int outbufferSize,
IntPtr bytesreturned, IntPtr overlapped
);
[DllImport("kernel32.dll")]
private static extern void CloseHandle(IntPtr hdl);
}

P-Invoke in .net core with Linux

PInvoke is certainly supported (that is how all of the code that interfaces with the OS works), but not in the specific case you listed. You cannot PInvoke to a win32 binary on Linux, unless you have somehow built a function-compatible version of it for Linux yourself. More realistically, you need to find a Linux function from some other system binary which exposes similar functionality, and then use that instead.

P/Invoke C# to C++

Are you using embedding (that is, you build your own executable that inits the mono runtime)? In that case the possibilitites are usually two:

  • You have a typo
  • The compiler/linker removed during optimization the function from your binary

To check for either, run:

nm your_program |grep Foobar

and see if a symbol with that name is present in the executable your_program.
If you see a mangled name it means extern "C" was not applied correctly in your code.

If you're not using embedding, you need to use the dynamic library name and not __Internal in DllImport (and check for typos and the above linker optimization issue as well).

P-invoke call fails if too much memory is assigned beforehand

Unmanaged code is prone to corrupt the heap. The side effects of that corruption are very unpredictable, it depends on what happens afterwards with that corrupted memory. It is not uncommon that nothing bad happens if the corruption is not in a crucial location. Changing the memory allocation pattern of your program can change that outcome.

All you really know right now is that the unmanaged code can't be trusted. Doing something about it is invariably hard, especially from a managed host program. You won't get anywhere until you start writing unit tests for that unmanaged code, using unmanaged code to exercise it, and find a reproducible bomb that you could tackle with an unmanaged debugger.

P-invoke call fails if too much memory is assigned beforehand

Unmanaged code is prone to corrupt the heap. The side effects of that corruption are very unpredictable, it depends on what happens afterwards with that corrupted memory. It is not uncommon that nothing bad happens if the corruption is not in a crucial location. Changing the memory allocation pattern of your program can change that outcome.

All you really know right now is that the unmanaged code can't be trusted. Doing something about it is invariably hard, especially from a managed host program. You won't get anywhere until you start writing unit tests for that unmanaged code, using unmanaged code to exercise it, and find a reproducible bomb that you could tackle with an unmanaged debugger.

Android goldfish kernel IOCTL system call hook kernel panic

Call stack address sys_ioctl+0x64/0x6c looks like ret command from the sys_ioctl.

Possible workflow which leads to kernel panic:

  1. Someone calls ioctl. According to syscall table it resolves into our_sys_ioctl.

  2. Function our_sys_ioctl calls sys_ioctl via pointer original_call_ioctl. Return address is stored in the stack, as usual.

  3. sys_ioctl at the end calls filesystem-specific .ioctl function, which may require a long time for execute(e.g., wait).

  4. Another process calls rmmod for your module. It frees code of our_sys_ioctl among other things.

  5. Filesystem specific .ioctl function returns into sys_ioctl. sys_ioctl executes its last ret instruction which jumps inside former our_sys_ioctl. But its code is already freed, so this leads to undefined behavior, which may cause kernel panic.

Such race condition may be eliminated by using tail call, when address, stored at step 2 on the stack, is actually return address of our_sys_ioctl.

But race between calling our_sys_ioctl and freeing code of this function is unavoidable. It is better to not unload module which replaces system call at all.



Related Topics



Leave a reply



Submit