-
Notifications
You must be signed in to change notification settings - Fork 201
Unix domain #306
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unix domain #306
Conversation
|
Two comments:
The idea of adding |
Network/Socket/Types.hsc
Outdated
| withNewSocketAddress :: SocketAddress sa => (Ptr sa -> Int -> IO a) -> IO a | ||
| withNewSocketAddress f = allocaBytes 128 $ \ptr -> f ptr 128 | ||
| withNewSocketAddress f = allocaBytes 128 $ \ptr -> do | ||
| zeroMemory ptr 128 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The constant 128 should probably be computed from max(sizeof(sockaddr_storage), sizeof(sockaddr_un)) or similar (some systems don't have a sockaddr_storage, and sockaddr_in and sockaddr_in6 might need to be used instead). And it is here that we might want to add and zero one more byte.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, memory is cheap. So, let's just use 128 which is the size of struct sockaddr_strage. +1 is not necessary for 128.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, so the idea is that 128 is large enough for sockaddr_un on all platforms, and naturally also for AF_INET and AF_INET6` socket addresses. Perhaps a comment to that effect and a named constant at the top of the file? I am a bit averse to unexplained magic numbers in code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
| poker = case path of ('\0':_) -> pokeArray; _ -> pokeArray0 0 | ||
| poker ((#ptr struct sockaddr_un, sun_path) p) pathC | ||
| -- the buffer is already filled with nulls. | ||
| pokeArray ((#ptr struct sockaddr_un, sun_path) p) pathC |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might not be safe when path is unexpectedly long. We now have sizeOfSockAddr for unix-domain sockets as a constant sizeof(sockaddr_un), and it seems possible that trying to use a longer path will corrupt memory?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested this with a short program that calls allocaBytes 128 and then pokeArray p with an array of > 128 C chars. The compiler and run-time did not object, and dutifully dumped core when the array was large enough. So here, we need to fail if SockAddrUnix path contains a path that is too long (which is a bit shorter than 128 bytes, since we have to account for the sun_family structure member. It may be unwise to assume that it is the standard 2 bytes, so we could either compute the overhead explicitly, or just cap the size at 120 (again as a named constant, properly commented at the top of the file).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now, throw an error.
|
|
||
| serverSetup = do | ||
| sock <- socket AF_UNIX Stream defaultProtocol | ||
| bind sock (SockAddrUnix unixAddr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing unlink before bind
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A more careful test might check whether the directory entry of that name (if it exists) is a socket (not a regular file or directory) and only unlink in that case, but "/tmp/network-socket" should be safe enough. We should probably also unlink when the test is done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unlink is good. But is it available on Windows?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be the same as the existing cleanup after the test.
E.tryJust (guard . isDoesNotExistError) $ removeFile unixAddr
Note also that Windows has no unix-domain sockets, so both the test and the unlink can be skipped on Windows.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done just in case.
tests/SimpleSpec.hs
Outdated
| killClientSock sock = do | ||
| shutdown sock ShutdownBoth | ||
| close sock | ||
| E.tryJust (guard . isDoesNotExistError) $ removeFile unixAddr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, there's the unlink after the test...
|
|
||
| unixPathMax :: Int | ||
| unixPathMax = 108 | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that on NetBSD, FreeBSD and Darwin the size of sun_path in sockaddr_un is 104 bytes. However, allocating larger sockaddr buffers (than sizeof(sockaddr_un)) allows use of longer (NUL-terminated) pathnames. So we can in fact bind and connect to sockets with names that are 108 (or more) bytes in length on these systems, provided the sockaddr buffer is long enough. The only issue is that not all clients will be able to interoperate with sockets whose names that exceed the default sizeof(sun_path) of sockaddr_un, but that's the user's problem if he/she chooses to create such sockets.
If there are still Linux systems that fail to NUL-terminate returned socket addresses, we should however promise to the OS one less byte than we allocated and zeroed:
withNewSocketAddress f = allocaBytes sockaddrStorageLen $ \ptr -> do
zeroMemory ptr $ fromIntegral sockaddrStorageLen
f ptr (sockaddrStorageLen-1)
This is likely needed everywhere, since maximal length pathnames might otherwise not get a NUL terminator even on BSD and Darwin.
That said, note that this pull request uses sockaddrStorageLen = 128 only to allocate storage for socket addresses we get back from the OS in withNewSocketAddress but still leaves
sizeOfSockAddr SockAddrUnix{} = #const sizeof(struct sockaddr_un)
as the size used in withSocketAddress to allocate socket addresses for passing to the OS. Therefore, on BSD and Darwin pathnames of 108 bytes will overflow the allocated buffer when binding paths of 108 bytes.
Since we need to support initialization of the same size sockets as we're willing to receive (and in particular path of up 108 bytes on BSD and the like, where this requires socket paths longer than fit in the stock sockaddr_un) we need to also allocate space for sockaddrStorageLen bytes when initializing AF_UNIX sockaddrs with a user provided path.
Since we'll refuse to actually write more than 108 bytes of path in pokeSockAddr, the result will always be NUL-terminated.
sizeOfSockAddr SockAddrUnix{} = #const sizeof(struct sockaddr_un)
to
sizeOfSockAddr SockAddrUnix{path} = sockaddrStorageLen
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for this investigation. I agree with your approach. So, I pushed the fix.
|
One more thing: in They should probably have been represented as ByteStrings, not Strings. If the API in 3.0 is different enough to allow that change, that might also make sense. |
|
I'm busy now. I will come back to this issue on 15th Feb. |
Good point. But in this case, clients are anonymous. So, |
Done.
I would like to discuss this kind of changes in a separate issue. If you wish, please record this issue. |
|
I'm waiting for results from CI. |
|
c3e2e2e does not work for Linux. |
|
Can you be more explicit about what breaks? |
|
See pull-request 1 against your unix-domain branch. |
|
@vdukhovni OK. Thank you for your thorough review. I think we can merge this now. @JonCoens I will merge this instead of #305 and try to backport this to 2.6. Thank you for reporting the bug. |
|
@vdukhovni Oh, you are assigned as a reviewer. Please approve this! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Separately there should probably be documentation that path in SockAddrUnix path, though defined as String, must hold only characters from 1-255, and is not subject to any encoding to/from String. Whether this warrants a type change to ByteString is less clear. That could do more harm than good.
|
Thanks. Merged. I will take care of doc stuff. |
Cc: @vdukhovni @JonCoens
Fix:
acceptshould returnSockAddrUnix ""for unix domain sockets.#const sizeof(struct sockaddr_un). If we add 1 to this size, the tests fail.