-
Notifications
You must be signed in to change notification settings - Fork 188
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
Unix domain #306
Conversation
Two comments:
The idea of adding |
Network/Socket/Types.hsc
Outdated
@@ -820,7 +820,9 @@ withSocketAddress addr f = do | |||
allocaBytes sz $ \p -> pokeSocketAddress p addr >> f (castPtr p) sz | |||
|
|||
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.
tests/SimpleSpec.hs
Outdated
|
||
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...
Network/Socket/Types.hsc
Outdated
@@ -910,6 +914,9 @@ withSockAddr addr f = do | |||
let sz = sizeOfSockAddr addr | |||
allocaBytes sz $ \p -> pokeSockAddr p addr >> f (castPtr p) sz | |||
|
|||
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:
accept
should returnSockAddrUnix ""
for unix domain sockets.#const sizeof(struct sockaddr_un)
. If we add 1 to this size, the tests fail.