POSIX getaddrinfo memory behaviour


(Etan Kissling) #1

POSIX getaddrinfo allocates memory that must later be freed using freeaddrinfo.
See http://manpages.ubuntu.com/manpages/xenial/en/man3/getaddrinfo.3.html

To simplify the API, I've created this function:

        import Foundation
        
        enum SystemError: Swift.Error {
            case getaddrinfo(Int32, Int32?)
        }
        
        public func getaddrinfo(node: String?, service: String?, hints: addrinfo?) throws -> [addrinfo] {
            var err: Int32
            var res: UnsafeMutablePointer<addrinfo>?
            if var hints = hints {
                err = getaddrinfo(node, service, &hints, &res)
            } else {
                err = getaddrinfo(node, service, nil, &res)
            }
            if err == EAI_SYSTEM {
                throw SystemError.getaddrinfo(err, errno)
            }
            if err != 0 {
                throw SystemError.getaddrinfo(err, nil)
            }
            defer {
                freeaddrinfo(res)
            }
            var result = [addrinfo]()
            var ai = res?.pointee
            while ai != nil {
                result.append(ai!)
                ai = ai!.ai_next?.pointee
            }
            return result
        }
        
I don't feel that the function is correct, though.

• How can the Swift memory model know that getaddrinfo allocates memory, and that Swift should not overwrite that memory with own stuff?
• How can Swift know that freeaddrinfo deletes the whole list, and that it should copy out ai information that has been assigned to the result array?

What's the correct way to interface with getaddrinfo?

Thanks

Etan


(Quinn “The Eskimo!”) #2

It’s definitely not correct. The problem is that each `addrinfo` structure in your function has pointers to memory that’s freed by the `freeaddrinfo()` call.

You have a bunch of options here:

A. avoid `getaddrinfo` — The POSIX ‘resolve then connect’ approach is deeply painful when dealing with the random mix of IPv4 and IPv6 addresses you find in the real world. I strongly recommend that you use a connect-by-name API instead, like CFSocketStream (which you access via `Stream.getStreamsToHost(withName:port:inputStream:outputStream:)`).

The only reason /not/ to do this is if you’re using the address list for something other than opening a TCP connection.

Even if you back end is BSD Sockets, you can extract the file descriptor from the stream once it’s connected.

B. capture the addresses referenced by each `addrinfo`

C. wrap the address list in a class that holds it in memory while you access it

All of these options work but, realistically, A is the easiest and the best.

Share and Enjoy

···

On 4 Oct 2016, at 17:46, Etan Kissling via swift-users <swift-users@swift.org> wrote:

I don't feel that the function is correct

--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Relations, Developer Technical Support, Core OS/Hardware


(Michael Ferenduros) #3

Swift won't mess with memory it does't own, and memory the addrinfo list
falls into that category.

The addrinfo you're accessing through pointee is a struct which means that
when you assign it somewhere you get a *copy* of the thing pointed to.
Swift owns that copy and will manage it properly, and leave the original
alone.

Fwiw, this is the wrapper I'm using for sockets right now:
https://github.com/mike-ferenduros/SwiftySockets

···

On Tuesday, October 4, 2016, Etan Kissling via swift-users < swift-users@swift.org> wrote:

POSIX getaddrinfo allocates memory that must later be freed using
freeaddrinfo.
See http://manpages.ubuntu.com/manpages/xenial/en/man3/getaddrinfo.3.html

To simplify the API, I've created this function:

        import Foundation

        enum SystemError: Swift.Error {
            case getaddrinfo(Int32, Int32?)
        }

        public func getaddrinfo(node: String?, service: String?, hints:
addrinfo?) throws -> [addrinfo] {
            var err: Int32
            var res: UnsafeMutablePointer<addrinfo>?
            if var hints = hints {
                err = getaddrinfo(node, service, &hints, &res)
            } else {
                err = getaddrinfo(node, service, nil, &res)
            }
            if err == EAI_SYSTEM {
                throw SystemError.getaddrinfo(err, errno)
            }
            if err != 0 {
                throw SystemError.getaddrinfo(err, nil)
            }
            defer {
                freeaddrinfo(res)
            }
            var result = [addrinfo]()
            var ai = res?.pointee
            while ai != nil {
                result.append(ai!)
                ai = ai!.ai_next?.pointee
            }
            return result
        }

I don't feel that the function is correct, though.

• How can the Swift memory model know that getaddrinfo allocates memory,
and that Swift should not overwrite that memory with own stuff?
• How can Swift know that freeaddrinfo deletes the whole list, and that it
should copy out ai information that has been assigned to the result array?

What's the correct way to interface with getaddrinfo?

Thanks

Etan

_______________________________________________
swift-users mailing list
swift-users@swift.org <javascript:;>
https://lists.swift.org/mailman/listinfo/swift-users


(Michael Ferenduros) #4

Ach, apologies - I forgot that addrinfo contains pointers. Ignore what I
said :slight_smile:

···

On Tuesday, October 4, 2016, Mike Ferenduros <mike.ferenduros@gmail.com> wrote:

Swift won't mess with memory it does't own, and memory the addrinfo list
falls into that category.

The addrinfo you're accessing through pointee is a struct which means
that when you assign it somewhere you get a *copy* of the thing pointed to.
Swift owns that copy and will manage it properly, and leave the original
alone.

Fwiw, this is the wrapper I'm using for sockets right now:
https://github.com/mike-ferenduros/SwiftySockets

On Tuesday, October 4, 2016, Etan Kissling via swift-users < > swift-users@swift.org > <javascript:_e(%7B%7D,'cvml','swift-users@swift.org');>> wrote:

POSIX getaddrinfo allocates memory that must later be freed using
freeaddrinfo.
See http://manpages.ubuntu.com/manpages/xenial/en/man3/getaddrinfo.3.html

To simplify the API, I've created this function:

        import Foundation

        enum SystemError: Swift.Error {
            case getaddrinfo(Int32, Int32?)
        }

        public func getaddrinfo(node: String?, service: String?, hints:
addrinfo?) throws -> [addrinfo] {
            var err: Int32
            var res: UnsafeMutablePointer<addrinfo>?
            if var hints = hints {
                err = getaddrinfo(node, service, &hints, &res)
            } else {
                err = getaddrinfo(node, service, nil, &res)
            }
            if err == EAI_SYSTEM {
                throw SystemError.getaddrinfo(err, errno)
            }
            if err != 0 {
                throw SystemError.getaddrinfo(err, nil)
            }
            defer {
                freeaddrinfo(res)
            }
            var result = [addrinfo]()
            var ai = res?.pointee
            while ai != nil {
                result.append(ai!)
                ai = ai!.ai_next?.pointee
            }
            return result
        }

I don't feel that the function is correct, though.

• How can the Swift memory model know that getaddrinfo allocates memory,
and that Swift should not overwrite that memory with own stuff?
• How can Swift know that freeaddrinfo deletes the whole list, and that
it should copy out ai information that has been assigned to the result
array?

What's the correct way to interface with getaddrinfo?

Thanks

Etan

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Etan Kissling) #5

Yup (BTW I'm fine with the "ai_next" item becoming useless in the copies. I just want to copy out the other parts in a way that gets copied reliably)

···

On 4 Oct 2016, at 19:26, Mike Ferenduros via swift-users <swift-users@swift.org<mailto:swift-users@swift.org>> wrote:

Ach, apologies - I forgot that addrinfo contains pointers. Ignore what I said :slight_smile:

On Tuesday, October 4, 2016, Mike Ferenduros <mike.ferenduros@gmail.com<mailto:mike.ferenduros@gmail.com>> wrote:
Swift won't mess with memory it does't own, and memory the addrinfo list falls into that category.

The addrinfo you're accessing through pointee is a struct which means that when you assign it somewhere you get a *copy* of the thing pointed to. Swift owns that copy and will manage it properly, and leave the original alone.

Fwiw, this is the wrapper I'm using for sockets right now:
https://github.com/mike-ferenduros/SwiftySockets

On Tuesday, October 4, 2016, Etan Kissling via swift-users <swift-users@swift.org<javascript:_e(%7B%7D,'cvml','swift-users@swift.org');>> wrote:
POSIX getaddrinfo allocates memory that must later be freed using freeaddrinfo.
See http://manpages.ubuntu.com/manpages/xenial/en/man3/getaddrinfo.3.html

To simplify the API, I've created this function:

        import Foundation

        enum SystemError: Swift.Error {
            case getaddrinfo(Int32, Int32?)
        }

        public func getaddrinfo(node: String?, service: String?, hints: addrinfo?) throws -> [addrinfo] {
            var err: Int32
            var res: UnsafeMutablePointer<addrinfo>?
            if var hints = hints {
                err = getaddrinfo(node, service, &hints, &res)
            } else {
                err = getaddrinfo(node, service, nil, &res)
            }
            if err == EAI_SYSTEM {
                throw SystemError.getaddrinfo(err, errno)
            }
            if err != 0 {
                throw SystemError.getaddrinfo(err, nil)
            }
            defer {
                freeaddrinfo(res)
            }
            var result = [addrinfo]()
            var ai = res?.pointee
            while ai != nil {
                result.append(ai!)
                ai = ai!.ai_next?.pointee
            }
            return result
        }

I don't feel that the function is correct, though.

• How can the Swift memory model know that getaddrinfo allocates memory, and that Swift should not overwrite that memory with own stuff?
• How can Swift know that freeaddrinfo deletes the whole list, and that it should copy out ai information that has been assigned to the result array?

What's the correct way to interface with getaddrinfo?

Thanks

Etan

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users
_______________________________________________
swift-users mailing list
swift-users@swift.org<mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users


(Etan Kissling) #6

Got an answer here:
http://stackoverflow.com/questions/39857435/swift-getaddrinfo

Current version:
http://pastebin.com/y7nDATSH

···

On 4 Oct 2016, at 19:30, Etan Kissling via swift-users <swift-users@swift.org<mailto:swift-users@swift.org>> wrote:

Yup (BTW I'm fine with the "ai_next" item becoming useless in the copies. I just want to copy out the other parts in a way that gets copied reliably)

On 4 Oct 2016, at 19:26, Mike Ferenduros via swift-users <swift-users@swift.org<mailto:swift-users@swift.org>> wrote:

Ach, apologies - I forgot that addrinfo contains pointers. Ignore what I said :slight_smile:

On Tuesday, October 4, 2016, Mike Ferenduros <mike.ferenduros@gmail.com<mailto:mike.ferenduros@gmail.com>> wrote:
Swift won't mess with memory it does't own, and memory the addrinfo list falls into that category.

The addrinfo you're accessing through pointee is a struct which means that when you assign it somewhere you get a *copy* of the thing pointed to. Swift owns that copy and will manage it properly, and leave the original alone.

Fwiw, this is the wrapper I'm using for sockets right now:
https://github.com/mike-ferenduros/SwiftySockets

On Tuesday, October 4, 2016, Etan Kissling via swift-users <swift-users@swift.org<javascript:_e(%7B%7D,'cvml','swift-users@swift.org');>> wrote:
POSIX getaddrinfo allocates memory that must later be freed using freeaddrinfo.
See http://manpages.ubuntu.com/manpages/xenial/en/man3/getaddrinfo.3.html

To simplify the API, I've created this function:

        import Foundation

        enum SystemError: Swift.Error {
            case getaddrinfo(Int32, Int32?)
        }

        public func getaddrinfo(node: String?, service: String?, hints: addrinfo?) throws -> [addrinfo] {
            var err: Int32
            var res: UnsafeMutablePointer<addrinfo>?
            if var hints = hints {
                err = getaddrinfo(node, service, &hints, &res)
            } else {
                err = getaddrinfo(node, service, nil, &res)
            }
            if err == EAI_SYSTEM {
                throw SystemError.getaddrinfo(err, errno)
            }
            if err != 0 {
                throw SystemError.getaddrinfo(err, nil)
            }
            defer {
                freeaddrinfo(res)
            }
            var result = [addrinfo]()
            var ai = res?.pointee
            while ai != nil {
                result.append(ai!)
                ai = ai!.ai_next?.pointee
            }
            return result
        }

I don't feel that the function is correct, though.

• How can the Swift memory model know that getaddrinfo allocates memory, and that Swift should not overwrite that memory with own stuff?
• How can Swift know that freeaddrinfo deletes the whole list, and that it should copy out ai information that has been assigned to the result array?

What's the correct way to interface with getaddrinfo?

Thanks

Etan

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users
_______________________________________________
swift-users mailing list
swift-users@swift.org<mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org<mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users