Lack of parameter check for getsockopt()

I was trying to call getsockopt(fd, SOL_LOCAL, LOCAL_PEERCRED, ...), and by mistake passed a wrong value for the second parameter where it should be SOL_LOCAL. But the call still succeeded. Then I did more experiments and passed more random values for the second parameter, all succeeded. It seems there is a lack of parameter check in the implementation of getsockopt() , where it should return errors if people pass invalid parameters instead of succeeding silently. Hope the Apple engineers can help to validate and fix it.

Answered by DTS Engineer in 828867022

This seems to be a bug in the getsockopt implementation within Unix domains sockets. Presuming you’re calling getsockopt on a Unix domain socket, the kernel dispatches your request to the Unix domain socket option handler. That checks the option but doesn’t check the level, so you get the Unix domain socket options — LOCAL_PEERCRED and friends — regardless of what level you supply.

I’d appreciate you filing a bug about this. Please post your bug number so that I can add my own analysis to it.

Congratulations on finding a kernel bug!

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"


Here’s my basic test code:

import Foundation
import System

func main() throws {
    let s = try FileDescriptor.socketPair(AF_UNIX, SOCK_STREAM, 0).0
    var cred = xucred()
    var credLen = MemoryLayout.size(ofValue: cred)
    print("before:", credLen)
    try s.getSocketOption(SOL_LOCAL, LOCAL_PEERCRED, &cred, optionLen: &credLen)
    print("after:", credLen)
}

try main()

Note This uses the QSocket code from Extra-ordinary Networking.

It prints:

before: 76
after: 76

indicating success.

If you change SOL_LOCAL to SOL_SOCKET it prints:

before: 76
after: 4

That’s because the SOL_SOCKET handling is done at the socket layer, before dispatching down to the Unix domain socket layer. The value of LOCAL_PEERCRED is 1 which maps to SO_DEBUG, so you get a CInt result.

But if you then change SOL_SOCKET to 12345 you get this:

before: 76
after: 76

indicating that it’s been treated as SOL_LOCAL / LOCAL_PEERCRED.

This seems to be a bug in the getsockopt implementation within Unix domains sockets. Presuming you’re calling getsockopt on a Unix domain socket, the kernel dispatches your request to the Unix domain socket option handler. That checks the option but doesn’t check the level, so you get the Unix domain socket options — LOCAL_PEERCRED and friends — regardless of what level you supply.

I’d appreciate you filing a bug about this. Please post your bug number so that I can add my own analysis to it.

Congratulations on finding a kernel bug!

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"


Here’s my basic test code:

import Foundation
import System

func main() throws {
    let s = try FileDescriptor.socketPair(AF_UNIX, SOCK_STREAM, 0).0
    var cred = xucred()
    var credLen = MemoryLayout.size(ofValue: cred)
    print("before:", credLen)
    try s.getSocketOption(SOL_LOCAL, LOCAL_PEERCRED, &cred, optionLen: &credLen)
    print("after:", credLen)
}

try main()

Note This uses the QSocket code from Extra-ordinary Networking.

It prints:

before: 76
after: 76

indicating success.

If you change SOL_LOCAL to SOL_SOCKET it prints:

before: 76
after: 4

That’s because the SOL_SOCKET handling is done at the socket layer, before dispatching down to the Unix domain socket layer. The value of LOCAL_PEERCRED is 1 which maps to SO_DEBUG, so you get a CInt result.

But if you then change SOL_SOCKET to 12345 you get this:

before: 76
after: 76

indicating that it’s been treated as SOL_LOCAL / LOCAL_PEERCRED.

Lack of parameter check for getsockopt()
 
 
Q