Alternative titles: FFI Gone Awry, Static Buffers Considered Harmful, Mutable State Considered Harmful, unsafePerformIO Considered Harmful
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 serious (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 and the developer has been notified. 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.
Edit: I often write a blog post as I am dealing with the item (sometimes a day before I bother to hit post). I incorrectly hit post with the implication I submitted a patch – not true, I just mentioned the issue to dons on IRC.