Contents

1 Verifying safety : lambdabot's approach

Since 2004, lambdabot has executed arbitrary strings of Haskell provided
by user's of various IRC channels, in particular, the
Haskell channel. In order to do this, a particular security policy is
required. The policy, and its implementation, is described here.

1.1 The policy

Only allow execution of pure Haskell expressions.

1.2 The implementation

The evaluator is essentially a function,

eval ::String->IOString

, which takes a random Haskell string, verifies it,

compiles it, and evaluates the result, returning a String representing
the result, back over the network.

The driver reads a String from the network, and then subjects it to a
simple test:

The expression is parsed as a Haskell 98 expression, hopefully preventing code injection (is this true? and can any string that can parse as a valid Haskell expression become something more sinister when put in a particular context?)

If the string parses as a Haskell 98 expression, the 'runplugs' process
is then forked to evaluate the string, and the following checks are put
in place:

Only a trusted module set is imported, avoiding unsafePerformIO and unsafeIOtoST and such like.

Module imports are disallowed

Time and space limitations on the runplugs process are set by the OS 'rlimit' facility

The expression type checked, enforcing lack of memory errors

Because the user code is not at the beginning of the file, malicious {-# LANGUAGE #-} and {-# OPTIONS #-} flags are ignored

Only -fextended-default-rules are allowed, as language extensions over H98.

The resulting .o file is dynamically linked only into the throw-away runplugs instance

Even if all went well, the first 2048 characters of the shown string are returned to the caller (no infinite output DoS)

A few other niceties are provided:

The expression is bound to a random identifier (harmless to guess), in order to allow nice line error messages with line pragmas.

The expression is wrapped in 'show'.

A catch-all instance of Show in terms of Typable is provided, to display non-displayable objects in a more useful way (e.g. putStrLn --> <[Char] -> IO ()>)

It is compiled to native code with -fasm for speed (compilation time is neglible compared to IRC lag)

The value is evaluated inside an exception handler; if an exception is thrown, the first 1024 characters of the exception string are returned.

2 Exploits

A variety of interesting exploits have been found, or thought of, over
the years. Those we remember are listed below:

using newtype recursion to have the inliner not terminate

using pathological type inference cases to have the type checker not terminate

code injection of code fragments that aren't Haskell expressions

Template Haskell used to run IO actions during type checking

stToIO to convert a safe ST action, into an IO action that is run

large strings returned in exceptions

unsafePerformIO, of course

unsafeCoerce#

throwing a piece of code as an exception, which is evaluated when the exception is shown