June 9, 2008
Yes, the owner of this blog is requesting to get added to Planet Haskell – is this authentication enough? ;-)
May 25, 2008
I posted this as a page by accident – so here it is as a blog entry and I’ll delete the page some day.
My previous post discussed how inet_ntoa uses a static buffer which can cause a race condition. Unlike in ‘C’, this is particularly likely to cause a race in Haskell programs due to the quick, easy, and cheap threads using forkIO that (potentially) share a single OS thread. Two bright spots were that inet_ntoa was marked as IO and that the result is usually unimportant.
Another FFI binding, nano-md5, has a similar race condition but is much more series (not marked as IO and the result is a digest).
An even-handed note: iirc, nano-md5 remains hackage mostly as an FFI example – not that this is advertised in the nano-md5 description. “Real” users are told to look at hsOpenSSL and hopenssl – a cursory glance at the code suggests they don’t have this bug. Also, the other bindings don’t require O(n) space – so they are certainly worth switching to.
The nano-md5 line:
digest <- c_md5 ptr (fromIntegral n) nullPtr
is the culprit. It uses ‘nullPtr’ and according to the OpenSSL manual “If md is NULL, the digest is placed in a static array”.
Test code that confirms the bug can be found here – this will run three hash operations in parallel and eventually one result will be the correct first bits with ending bits from one of the other digests. The developer has already fixed the issue for versions 0.1.2+. I’ll wrap this post up with a request for library developers to please work to avoid use of static buffers – they have no place in this forkIO happy playland I call Haskell.
April 24, 2008
Just because I am feeling lazy wrt any real task, I decided to post about the sillyness that is inet_ntoa. Yes, this is ancient/known stuff to rehash, but you can hit the browser back button at any time.
As most of you probably know, the function inet_ntoa converts an IPv4 address to ascii, storing the result in a static buffer. It is this last part that periodically causes people fun when they forget. This mutable memory issue is revealed easily enough in goofed up ‘C’ statements such as:
struct in_addr a,b; inet_aton("126.96.36.199", &a); inet_aton("188.8.131.52", &b); printf("addr a: %s\taddr b: %s\n", inet_ntoa(a), inet_ntoa(b));
which returns “addr a: 184.108.40.206 addr b: 220.127.116.11″. Sometimes more complex systems have a race condition (ex: exception handlers calling inet_ntoa), but it isn’t a larger issue in multi-threaded C programs thanks to thread local storage…
unless you cram many logical threads into a single OS thread like in Haskell. Zao in #haskell asked why inet_ntoa was of type IO (meaning, it isn’t a pure / deterministic function) and I correctly guessed it was a wrapper for the ‘C’ call.
Not to rip at any of the libraries folk, who made a faithful foreign function interface for the sockets/networking functions, but – this was a bad idea. Foremost, the use of IO means this can’t be called from any deterministic function even though the desired operation of converting an address to a string IS deterministic. Secondly, some Haskell programmers (myself included) use Haskells threads liberally (perhaps another, positive, blog post on that). So if someone is being brain-dead then they are going to have a bug – likely non-fatal and obvious due to how string representation of addresses are used.
And if you desire to see the race, I have some code… hope it runs… yep:
import Network.Socket (inet_ntoa) import Control.Concurrent (forkIO, threadDelay) import Control.Monad (when, forever) main = do let zero = (0,"0.0.0.0") one = (1,"18.104.22.168") two = (2,"22.214.171.124") assert = confirm "assert" race = \x -> forever (confirm "FAILURE: " x) assert zero assert one assert two forkIO $ race zero forkIO $ race one forkIO $ race two threadDelay maxBound test n s = forever $ confirm "" (n,s) confirm prefix (n,str) = do s <- inet_ntoa n when (s /= str) (error (prefix ++ s ++ " does not equal " ++ str))
Yes, I know this non-deterministic behavior is being screamed by that ‘IO’ type.
Yes, I know I should write a Haskellized network library.