Home > Blockchain >  How to send data as input with IOConnectCallStructMethod
How to send data as input with IOConnectCallStructMethod

Time:12-21

I'm trying to use IOConnectCallStructMethod so send data in the input field to talk to my driver in DriverKit (iPadOS)

In this particular method I send an input and I expect getting an output. But ignore the output for now.

In my case IOConnectCallStructMethod is called with Swift:

var input:[UInt8] = Array(repeating: 0, count: 200)
for i in 0..<200 {
    if (i % 2 == 0) {
        input[i] = UInt8(i)
    }
}
let arraySize:Int = input.count;
var outputSize = MemoryLayout<UInt8>.size * arraySize
var output:[UInt8] = Array(repeating: 0, count: arraySize)

let inputSize = MemoryLayout<UInt8>.size * input.count

let ret = IOConnectCallStructMethod(connection, Selector.mySelector.rawValue, &input, inputSize, &output, &outputSize)

This is the dispatch in the external method:

[ExternalMethodType_MyMethodRequest] =
{
    .function = (IOUserClientMethodFunction) &Client::StaticHandleRequest,
    .checkCompletionExists = false,
    .checkScalarInputCount = 0,
    .checkStructureInputSize = 200,
    .checkScalarOutputCount = 0,
    .checkStructureOutputSize = 200,
},

The ExternalMethod gets called and when I try to access to the input data with this:

char* input = nullptr;
size_t length = 0;
if (arguments->structureInput != nullptr)
{
    input = (char*)arguments->structureInput->getBytesNoCopy();
    length = arguments->structureInput->getLength();
    Log("Input: %s", input);
    Log("length: %zu", length);
}

If I check the variable input with a breakpoint the value is always "" and the length is 200

I tried adding the input in a struct, same result. I tried using IOConnectCallMethod, same result.

The only alternative I can do it's making the input data extra big to force the system to use the descriptor instead of the OSData, but the input in this case would be always much smaller, so not sure if the best way to go.

Interestingly if I print the output of input with:

#define Log(fmt, ...) os_log(OS_LOG_DEFAULT, "NullDriver - " fmt "\n", ##__VA_ARGS__)

It will give me private:

NullDriver - Input: <private>

Maybe the data is actually there but I can't see it?

CodePudding user response:

I think the data is there but I can't see it.

If I do:

char realInput[200];
memcpy(realInput, input, 200);

When I loop printing in the debugger: realInput[i] I get my precious data

CodePudding user response:

Further to discussion in comments:

There is no issue here with the way the data is being sent from app to dext, but rather with the way it is being logged/printed: a format string of %s is not a suitable way of printing the contents of a byte array. That treats it as a nul-terminated string (UTF-8, usually). It looks like you're sending arbitrary bytes, not a human readable string. In particular, your first byte is 0, which is how an empty C string is represented, and hence your empty output.

A format string such as "x x x x" with arguments input[0], input[1], input[2], input[3] would be more appropriate for printing the first 4 bytes; you'll have to do this in a loop, possibly writing to a buffer using snprintf before sending it to the system log, to print the entire array.

Separately: to fix the <private> issue when using %s, use %{public}s. (but read the os_log documentation on the privacy ramifications of this before you ship it)

  • Related