2

All documentation and examples say that if a socket's CFSocketCallBack callout is given a .dataCallback as its second parameter (callbackType), that implies that the fourth one (data) can be cast to a CFData object containing all the data pre-read from the socket.

However, when I try to do this, it fails:

private func myCallout(socket: CFSocket?,
                       callBackType: CFSocketCallBackType,
                       address: CFData?,
                       callBackTypeMetaData: UnsafeRawPointer?,
                       info: UnsafeMutableRawPointer?) -> Void {

    // Behavior inferred from Developer
    // https://developer.apple.com/documentation/corefoundation/cfsocketcallback

    guard
        let info = info,
        let socket = socket
        else {
        return assertionFailure("Socket may have gone out of scope before response")
    }

    // <REDACTED>


    if callBackType == .dataCallBack {
        guard let data = callBackTypeMetaData?.load(as: CFData.self) else {
            return assertionFailure("Data Callback's metadata was not a CFData object")
        }

        foo(data as Data) // Crashes here
    }

    // <REDACTED>
}

The crash I get is this:

Thread 1: EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0).

In the debugger, when I type this:

(lldb) po callBackTypeMetaData!.load(as: CFData.self)

it just prints a pointer address (rather high, also; only two significant 0s). However, when I type this:

(lldb) po callBackTypeMetaData!.load(as: CFData.self) as Data

It prints this:

error: Execution was interrupted, reason: EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0).
The process has been returned to the state before expression evaluation.

So it seems what I got is not really a CFData, but it's close enough to seem similar to it on the surface, until something actually tries to read it as such.

So what's going on here, where is my data, and how do I fix this?

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
Ky -
  • 30,724
  • 51
  • 192
  • 308

1 Answers1

2
data = callBackTypeMetaData!.load(as: CFData.self)

reads a CFData pointer from the memory location that callBackTypeMetaData! points to. What you want is to "reinterpret" the raw pointer as a CFData (or NSData) pointer:

if callBackType == .dataCallBack {
    if let rawPointer = callBackTypeMetaData  {
        let data = Unmanaged<NSData>.fromOpaque(rawPointer).takeUnretainedValue() as Data
        // ...
    }
}
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • That definitely eliminated the crash! But I don't quite understand why... I thought that "Treating memory at a pointer's address as some type" was the same as "reinterpreting a raw pointer as some type". How are these two approaches different, and how can I know this difference in the future? – Ky - Dec 12 '17 at 20:00
  • Also concerningly, when I print out `data` in the debugger, it says the byte count is `0` and the pointer is `0x000000000000bad0` on every run – Ky - Dec 12 '17 at 20:02
  • 1
    @BenLeggiero: Your code loads `data` from the memory location that `callBackTypeMetaData!` points to. My code just assigns `data` to the value of `callBackTypeMetaData!`, the rest is type casting for the compiler. The documentation says that the raw pointer "is a" CFData object, not that it "points to" a CFData object. – Martin R Dec 12 '17 at 20:03
  • Ah, I see. It's another reference vs. value thing – Ky - Dec 12 '17 at 20:04
  • I ran into this always being an empty Data object and I can't figure out where I'm going wrong... :/ – Ky - Dec 19 '17 at 21:01
  • @BenLeggiero: Do you by any chance have a minimal sample project that you can share? Then I would look into it. – Martin R Dec 19 '17 at 21:02
  • Anticipating this, I rewrote the whole project as a "SSCCE" over the weekend. It's a bit dirty and such, but it serves the purpose of replicating the important parts of the setup: https://github.com/BenLeggiero/Socket-IPC-SSCCE-2017-12-15 – Ky - Dec 19 '17 at 21:40
  • @BenLeggiero: It works in my test. Here is a minimal command-line socket client which I ran against "netcat" as the server: https://gist.github.com/anonymous/0a817aab1bd8fc00a9cd87601aa70c94. – In your project there is no registration for `.dataCallBack` at all, so that case wouldn't be hit anyway. – Martin R Dec 20 '17 at 22:48
  • Whoops! I'd committed it in the middle of some test changes. I've updated it to add data callbacks when creating the sockets: [Client](https://github.com/BenLeggiero/Socket-IPC-SSCCE-2017-12-15/blob/master/Socket%20IPC%20SSCCE%202017-12-15/SSCCE%20for%20SO.swift#L28) | [Server](https://github.com/BenLeggiero/Socket-IPC-SSCCE-2017-12-15/blob/master/Socket%20IPC%20SSCCE%202017-12-15/SSCCE%20for%20SO.swift#L142) | [Diff](https://github.com/BenLeggiero/Socket-IPC-SSCCE-2017-12-15/commit/091bcc4437197ec3b19e410453aeb39e41fab929) – Ky - Dec 21 '17 at 21:29
  • @BenLeggiero: Did you try my test program? It demonstrates that the answer is correct. – Martin R Dec 21 '17 at 21:30