protect memory access with traps

This commit is contained in:
Ilya Rezvov
2018-04-21 10:11:37 -07:00
parent e99c7c427b
commit 7726eb7505
2 changed files with 145 additions and 60 deletions
+93 -8
View File
@@ -720,6 +720,9 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let readByte idx = do let readByte idx = do
byte <- IOVector.read memory $ addr + idx byte <- IOVector.read memory $ addr + idx
return $ fromIntegral byte `shiftL` (idx * 8) return $ fromIntegral byte `shiftL` (idx * 8)
if addr + 4 > IOVector.length memory
then return Trap
else do
val <- sum <$> mapM readByte [0..3] val <- sum <$> mapM readByte [0..3]
return $ Done ctx { stack = VI32 val : rest } return $ Done ctx { stack = VI32 val : rest }
step ctx@EvalCtx{ stack = (VI32 v:rest) } (I64Load MemArg { offset }) = do step ctx@EvalCtx{ stack = (VI32 v:rest) } (I64Load MemArg { offset }) = do
@@ -729,6 +732,9 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let readByte idx = do let readByte idx = do
byte <- IOVector.read memory $ addr + idx byte <- IOVector.read memory $ addr + idx
return $ fromIntegral byte `shiftL` (idx * 8) return $ fromIntegral byte `shiftL` (idx * 8)
if addr + 8 > IOVector.length memory
then return Trap
else do
val <- sum <$> mapM readByte [0..7] val <- sum <$> mapM readByte [0..7]
return $ Done ctx { stack = VI64 val : rest } return $ Done ctx { stack = VI64 val : rest }
step ctx@EvalCtx{ stack = (VI32 v:rest) } (F32Load MemArg { offset }) = do step ctx@EvalCtx{ stack = (VI32 v:rest) } (F32Load MemArg { offset }) = do
@@ -738,6 +744,9 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let readByte idx = do let readByte idx = do
byte <- IOVector.read memory $ addr + idx byte <- IOVector.read memory $ addr + idx
return $ fromIntegral byte `shiftL` (idx * 8) return $ fromIntegral byte `shiftL` (idx * 8)
if addr + 4 > IOVector.length memory
then return Trap
else do
val <- wordToFloat . sum <$> mapM readByte [0..3] val <- wordToFloat . sum <$> mapM readByte [0..3]
return $ Done ctx { stack = VF32 val : rest } return $ Done ctx { stack = VF32 val : rest }
step ctx@EvalCtx{ stack = (VI32 v:rest) } (F64Load MemArg { offset }) = do step ctx@EvalCtx{ stack = (VI32 v:rest) } (F64Load MemArg { offset }) = do
@@ -747,18 +756,27 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let readByte idx = do let readByte idx = do
byte <- IOVector.read memory $ addr + idx byte <- IOVector.read memory $ addr + idx
return $ fromIntegral byte `shiftL` (idx * 8) return $ fromIntegral byte `shiftL` (idx * 8)
if addr + 8 > IOVector.length memory
then return Trap
else do
val <- wordToDouble . sum <$> mapM readByte [0..7] val <- wordToDouble . sum <$> mapM readByte [0..7]
return $ Done ctx { stack = VF64 val : rest } return $ Done ctx { stack = VF64 val : rest }
step ctx@EvalCtx{ stack = (VI32 v:rest) } (I32Load8U MemArg { offset }) = do step ctx@EvalCtx{ stack = (VI32 v:rest) } (I32Load8U MemArg { offset }) = do
let MemoryInstance { memory = memoryRef } = memInstances store ! (memaddrs moduleInstance ! 0) let MemoryInstance { memory = memoryRef } = memInstances store ! (memaddrs moduleInstance ! 0)
memory <- readIORef memoryRef memory <- readIORef memoryRef
let addr = fromIntegral $ v + fromIntegral offset let addr = fromIntegral $ v + fromIntegral offset
if addr + 1 > IOVector.length memory
then return Trap
else do
byte <- IOVector.read memory addr byte <- IOVector.read memory addr
return $ Done ctx { stack = VI32 (fromIntegral byte) : rest } return $ Done ctx { stack = VI32 (fromIntegral byte) : rest }
step ctx@EvalCtx{ stack = (VI32 v:rest) } (I32Load8S MemArg { offset }) = do step ctx@EvalCtx{ stack = (VI32 v:rest) } (I32Load8S MemArg { offset }) = do
let MemoryInstance { memory = memoryRef } = memInstances store ! (memaddrs moduleInstance ! 0) let MemoryInstance { memory = memoryRef } = memInstances store ! (memaddrs moduleInstance ! 0)
memory <- readIORef memoryRef memory <- readIORef memoryRef
let addr = fromIntegral $ v + fromIntegral offset let addr = fromIntegral $ v + fromIntegral offset
if addr + 4 > IOVector.length memory
then return Trap
else do
byte <- IOVector.read memory addr byte <- IOVector.read memory addr
let val = asWord32 $ if byte >= 128 then -1 * fromIntegral (0xFF - byte + 1) else fromIntegral byte let val = asWord32 $ if byte >= 128 then -1 * fromIntegral (0xFF - byte + 1) else fromIntegral byte
return $ Done ctx { stack = VI32 val : rest } return $ Done ctx { stack = VI32 val : rest }
@@ -769,6 +787,9 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let readByte idx = do let readByte idx = do
byte <- IOVector.read memory $ addr + idx byte <- IOVector.read memory $ addr + idx
return $ fromIntegral byte `shiftL` (idx * 8) return $ fromIntegral byte `shiftL` (idx * 8)
if addr + 2 > IOVector.length memory
then return Trap
else do
val <- sum <$> mapM readByte [0..1] val <- sum <$> mapM readByte [0..1]
return $ Done ctx { stack = VI32 val : rest } return $ Done ctx { stack = VI32 val : rest }
step ctx@EvalCtx{ stack = (VI32 v:rest) } (I32Load16S MemArg { offset }) = do step ctx@EvalCtx{ stack = (VI32 v:rest) } (I32Load16S MemArg { offset }) = do
@@ -778,6 +799,9 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let readByte idx = do let readByte idx = do
byte <- IOVector.read memory $ addr + idx byte <- IOVector.read memory $ addr + idx
return $ (fromIntegral byte :: Word32) `shiftL` (idx * 8) return $ (fromIntegral byte :: Word32) `shiftL` (idx * 8)
if addr + 2 > IOVector.length memory
then return Trap
else do
val <- sum <$> mapM readByte [0..1] val <- sum <$> mapM readByte [0..1]
let signed = asWord32 $ if val >= 2 ^ 15 then -1 * fromIntegral (0xFFFF - val + 1) else fromIntegral val let signed = asWord32 $ if val >= 2 ^ 15 then -1 * fromIntegral (0xFFFF - val + 1) else fromIntegral val
return $ Done ctx { stack = VI32 signed : rest } return $ Done ctx { stack = VI32 signed : rest }
@@ -785,12 +809,18 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let MemoryInstance { memory = memoryRef } = memInstances store ! (memaddrs moduleInstance ! 0) let MemoryInstance { memory = memoryRef } = memInstances store ! (memaddrs moduleInstance ! 0)
memory <- readIORef memoryRef memory <- readIORef memoryRef
let addr = fromIntegral $ v + fromIntegral offset let addr = fromIntegral $ v + fromIntegral offset
if addr + 1 > IOVector.length memory
then return Trap
else do
byte <- IOVector.read memory addr byte <- IOVector.read memory addr
return $ Done ctx { stack = VI64 (fromIntegral byte) : rest } return $ Done ctx { stack = VI64 (fromIntegral byte) : rest }
step ctx@EvalCtx{ stack = (VI32 v:rest) } (I64Load8S MemArg { offset }) = do step ctx@EvalCtx{ stack = (VI32 v:rest) } (I64Load8S MemArg { offset }) = do
let MemoryInstance { memory = memoryRef } = memInstances store ! (memaddrs moduleInstance ! 0) let MemoryInstance { memory = memoryRef } = memInstances store ! (memaddrs moduleInstance ! 0)
memory <- readIORef memoryRef memory <- readIORef memoryRef
let addr = fromIntegral $ v + fromIntegral offset let addr = fromIntegral $ v + fromIntegral offset
if addr + 1 > IOVector.length memory
then return Trap
else do
byte <- IOVector.read memory addr byte <- IOVector.read memory addr
let val = asWord64 $ if byte >= 128 then -1 * fromIntegral (0xFF - byte + 1) else fromIntegral byte let val = asWord64 $ if byte >= 128 then -1 * fromIntegral (0xFF - byte + 1) else fromIntegral byte
return $ Done ctx { stack = VI64 val : rest } return $ Done ctx { stack = VI64 val : rest }
@@ -801,6 +831,9 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let readByte idx = do let readByte idx = do
byte <- IOVector.read memory $ addr + idx byte <- IOVector.read memory $ addr + idx
return $ fromIntegral byte `shiftL` (idx * 8) return $ fromIntegral byte `shiftL` (idx * 8)
if addr + 2 > IOVector.length memory
then return Trap
else do
val <- sum <$> mapM readByte [0..1] val <- sum <$> mapM readByte [0..1]
return $ Done ctx { stack = VI64 val : rest } return $ Done ctx { stack = VI64 val : rest }
step ctx@EvalCtx{ stack = (VI32 v:rest) } (I64Load16S MemArg { offset }) = do step ctx@EvalCtx{ stack = (VI32 v:rest) } (I64Load16S MemArg { offset }) = do
@@ -810,6 +843,9 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let readByte idx = do let readByte idx = do
byte <- IOVector.read memory $ addr + idx byte <- IOVector.read memory $ addr + idx
return $ (fromIntegral byte :: Word32) `shiftL` (idx * 8) return $ (fromIntegral byte :: Word32) `shiftL` (idx * 8)
if addr + 2 > IOVector.length memory
then return Trap
else do
val <- sum <$> mapM readByte [0..1] val <- sum <$> mapM readByte [0..1]
let signed = asWord64 $ if val >= 2 ^ 15 then -1 * fromIntegral (0xFFFF - val + 1) else fromIntegral val let signed = asWord64 $ if val >= 2 ^ 15 then -1 * fromIntegral (0xFFFF - val + 1) else fromIntegral val
return $ Done ctx { stack = VI64 signed : rest } return $ Done ctx { stack = VI64 signed : rest }
@@ -820,6 +856,9 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let readByte idx = do let readByte idx = do
byte <- IOVector.read memory $ addr + idx byte <- IOVector.read memory $ addr + idx
return $ fromIntegral byte `shiftL` (idx * 8) return $ fromIntegral byte `shiftL` (idx * 8)
if addr + 4 > IOVector.length memory
then return Trap
else do
val <- sum <$> mapM readByte [0..3] val <- sum <$> mapM readByte [0..3]
return $ Done ctx { stack = VI64 val : rest } return $ Done ctx { stack = VI64 val : rest }
step ctx@EvalCtx{ stack = (VI32 v:rest) } (I64Load32S MemArg { offset }) = do step ctx@EvalCtx{ stack = (VI32 v:rest) } (I64Load32S MemArg { offset }) = do
@@ -829,6 +868,9 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let readByte idx = do let readByte idx = do
byte <- IOVector.read memory $ addr + idx byte <- IOVector.read memory $ addr + idx
return $ (fromIntegral byte :: Word32) `shiftL` (idx * 8) return $ (fromIntegral byte :: Word32) `shiftL` (idx * 8)
if addr + 4 > IOVector.length memory
then return Trap
else do
val <- sum <$> mapM readByte [0..3] val <- sum <$> mapM readByte [0..3]
let signed = asWord64 $ fromIntegral $ asInt32 val let signed = asWord64 $ fromIntegral $ asInt32 val
return $ Done ctx { stack = VI64 signed : rest } return $ Done ctx { stack = VI64 signed : rest }
@@ -839,6 +881,9 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let writeByte idx = do let writeByte idx = do
let byte = fromIntegral $ v `shiftR` (idx * 8) .&. 0xFF let byte = fromIntegral $ v `shiftR` (idx * 8) .&. 0xFF
IOVector.write memory (addr + idx) byte IOVector.write memory (addr + idx) byte
if addr + 4 > IOVector.length memory
then return Trap
else do
mapM_ writeByte [0..3] mapM_ writeByte [0..3]
return $ Done ctx { stack = rest } return $ Done ctx { stack = rest }
step ctx@EvalCtx{ stack = (VI64 v:VI32 va:rest) } (I64Store MemArg { offset }) = do step ctx@EvalCtx{ stack = (VI64 v:VI32 va:rest) } (I64Store MemArg { offset }) = do
@@ -848,6 +893,9 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let writeByte idx = do let writeByte idx = do
let byte = fromIntegral $ v `shiftR` (idx * 8) .&. 0xFF let byte = fromIntegral $ v `shiftR` (idx * 8) .&. 0xFF
IOVector.write memory (addr + idx) byte IOVector.write memory (addr + idx) byte
if addr + 8 > IOVector.length memory
then return Trap
else do
mapM_ writeByte [0..7] mapM_ writeByte [0..7]
return $ Done ctx { stack = rest } return $ Done ctx { stack = rest }
step ctx@EvalCtx{ stack = (VF32 f:VI32 va:rest) } (F32Store MemArg { offset }) = do step ctx@EvalCtx{ stack = (VF32 f:VI32 va:rest) } (F32Store MemArg { offset }) = do
@@ -858,6 +906,9 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let writeByte idx = do let writeByte idx = do
let byte = fromIntegral $ v `shiftR` (idx * 8) .&. 0xFF let byte = fromIntegral $ v `shiftR` (idx * 8) .&. 0xFF
IOVector.write memory (addr + idx) byte IOVector.write memory (addr + idx) byte
if addr + 4 > IOVector.length memory
then return Trap
else do
mapM_ writeByte [0..3] mapM_ writeByte [0..3]
return $ Done ctx { stack = rest } return $ Done ctx { stack = rest }
step ctx@EvalCtx{ stack = (VF64 f:VI32 va:rest) } (F64Store MemArg { offset }) = do step ctx@EvalCtx{ stack = (VF64 f:VI32 va:rest) } (F64Store MemArg { offset }) = do
@@ -868,6 +919,9 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let writeByte idx = do let writeByte idx = do
let byte = fromIntegral $ v `shiftR` (idx * 8) .&. 0xFF let byte = fromIntegral $ v `shiftR` (idx * 8) .&. 0xFF
IOVector.write memory (addr + idx) byte IOVector.write memory (addr + idx) byte
if addr + 8 > IOVector.length memory
then return Trap
else do
mapM_ writeByte [0..7] mapM_ writeByte [0..7]
return $ Done ctx { stack = rest } return $ Done ctx { stack = rest }
step ctx@EvalCtx{ stack = (VI32 v:VI32 va:rest) } (I32Store8 MemArg { offset }) = do step ctx@EvalCtx{ stack = (VI32 v:VI32 va:rest) } (I32Store8 MemArg { offset }) = do
@@ -877,6 +931,9 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let writeByte idx = do let writeByte idx = do
let byte = fromIntegral $ v `shiftR` (idx * 8) .&. 0xFF let byte = fromIntegral $ v `shiftR` (idx * 8) .&. 0xFF
IOVector.write memory (addr + idx) byte IOVector.write memory (addr + idx) byte
if addr + 1 > IOVector.length memory
then return Trap
else do
mapM_ writeByte [0] mapM_ writeByte [0]
return $ Done ctx { stack = rest } return $ Done ctx { stack = rest }
step ctx@EvalCtx{ stack = (VI32 v:VI32 va:rest) } (I32Store16 MemArg { offset }) = do step ctx@EvalCtx{ stack = (VI32 v:VI32 va:rest) } (I32Store16 MemArg { offset }) = do
@@ -886,6 +943,9 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let writeByte idx = do let writeByte idx = do
let byte = fromIntegral $ v `shiftR` (idx * 8) .&. 0xFF let byte = fromIntegral $ v `shiftR` (idx * 8) .&. 0xFF
IOVector.write memory (addr + idx) byte IOVector.write memory (addr + idx) byte
if addr + 2 > IOVector.length memory
then return Trap
else do
mapM_ writeByte [0, 1] mapM_ writeByte [0, 1]
return $ Done ctx { stack = rest } return $ Done ctx { stack = rest }
step ctx@EvalCtx{ stack = (VI64 v:VI32 va:rest) } (I64Store8 MemArg { offset }) = do step ctx@EvalCtx{ stack = (VI64 v:VI32 va:rest) } (I64Store8 MemArg { offset }) = do
@@ -895,6 +955,9 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let writeByte idx = do let writeByte idx = do
let byte = fromIntegral $ v `shiftR` (idx * 8) .&. 0xFF let byte = fromIntegral $ v `shiftR` (idx * 8) .&. 0xFF
IOVector.write memory (addr + idx) byte IOVector.write memory (addr + idx) byte
if addr + 1 > IOVector.length memory
then return Trap
else do
mapM_ writeByte [0] mapM_ writeByte [0]
return $ Done ctx { stack = rest } return $ Done ctx { stack = rest }
step ctx@EvalCtx{ stack = (VI64 v:VI32 va:rest) } (I64Store16 MemArg { offset }) = do step ctx@EvalCtx{ stack = (VI64 v:VI32 va:rest) } (I64Store16 MemArg { offset }) = do
@@ -904,6 +967,9 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let writeByte idx = do let writeByte idx = do
let byte = fromIntegral $ v `shiftR` (idx * 8) .&. 0xFF let byte = fromIntegral $ v `shiftR` (idx * 8) .&. 0xFF
IOVector.write memory (addr + idx) byte IOVector.write memory (addr + idx) byte
if addr + 2 > IOVector.length memory
then return Trap
else do
mapM_ writeByte [0, 1] mapM_ writeByte [0, 1]
return $ Done ctx { stack = rest } return $ Done ctx { stack = rest }
step ctx@EvalCtx{ stack = (VI64 v:VI32 va:rest) } (I64Store32 MemArg { offset }) = do step ctx@EvalCtx{ stack = (VI64 v:VI32 va:rest) } (I64Store32 MemArg { offset }) = do
@@ -913,6 +979,9 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
let writeByte idx = do let writeByte idx = do
let byte = fromIntegral $ v `shiftR` (idx * 8) .&. 0xFF let byte = fromIntegral $ v `shiftR` (idx * 8) .&. 0xFF
IOVector.write memory (addr + idx) byte IOVector.write memory (addr + idx) byte
if addr + 4 > IOVector.length memory
then return Trap
else do
mapM_ writeByte [0..3] mapM_ writeByte [0..3]
return $ Done ctx { stack = rest } return $ Done ctx { stack = rest }
step ctx@EvalCtx{ stack = st } CurrentMemory = do step ctx@EvalCtx{ stack = st } CurrentMemory = do
@@ -1153,21 +1222,37 @@ eval store FunctionInstance { funcType, moduleInstance, code = Function { localT
step ctx@EvalCtx{ stack = (VI64 v:rest) } I32WrapI64 = step ctx@EvalCtx{ stack = (VI64 v:rest) } I32WrapI64 =
return $ Done ctx { stack = VI32 (fromIntegral $ v .&. 0xFFFFFFFF) : rest } return $ Done ctx { stack = VI32 (fromIntegral $ v .&. 0xFFFFFFFF) : rest }
step ctx@EvalCtx{ stack = (VF32 v:rest) } (ITruncFU BS32 BS32) = step ctx@EvalCtx{ stack = (VF32 v:rest) } (ITruncFU BS32 BS32) =
return $ Done ctx { stack = VI32 (truncate v) : rest } if isNaN v
then return Trap
else return $ Done ctx { stack = VI32 (truncate v) : rest }
step ctx@EvalCtx{ stack = (VF64 v:rest) } (ITruncFU BS32 BS64) = step ctx@EvalCtx{ stack = (VF64 v:rest) } (ITruncFU BS32 BS64) =
return $ Done ctx { stack = VI32 (truncate v) : rest } if isNaN v
then return Trap
else return $ Done ctx { stack = VI32 (truncate v) : rest }
step ctx@EvalCtx{ stack = (VF32 v:rest) } (ITruncFU BS64 BS32) = step ctx@EvalCtx{ stack = (VF32 v:rest) } (ITruncFU BS64 BS32) =
return $ Done ctx { stack = VI64 (truncate v) : rest } if isNaN v
then return Trap
else return $ Done ctx { stack = VI64 (truncate v) : rest }
step ctx@EvalCtx{ stack = (VF64 v:rest) } (ITruncFU BS64 BS64) = step ctx@EvalCtx{ stack = (VF64 v:rest) } (ITruncFU BS64 BS64) =
return $ Done ctx { stack = VI64 (truncate v) : rest } if isNaN v
then return Trap
else return $ Done ctx { stack = VI64 (truncate v) : rest }
step ctx@EvalCtx{ stack = (VF32 v:rest) } (ITruncFS BS32 BS32) = step ctx@EvalCtx{ stack = (VF32 v:rest) } (ITruncFS BS32 BS32) =
return $ Done ctx { stack = VI32 (asWord32 $ truncate v) : rest } if isNaN v
then return Trap
else return $ Done ctx { stack = VI32 (asWord32 $ truncate v) : rest }
step ctx@EvalCtx{ stack = (VF64 v:rest) } (ITruncFS BS32 BS64) = step ctx@EvalCtx{ stack = (VF64 v:rest) } (ITruncFS BS32 BS64) =
return $ Done ctx { stack = VI32 (asWord32 $ truncate v) : rest } if isNaN v
then return Trap
else return $ Done ctx { stack = VI32 (asWord32 $ truncate v) : rest }
step ctx@EvalCtx{ stack = (VF32 v:rest) } (ITruncFS BS64 BS32) = step ctx@EvalCtx{ stack = (VF32 v:rest) } (ITruncFS BS64 BS32) =
return $ Done ctx { stack = VI64 (asWord64 $ truncate v) : rest } if isNaN v
then return Trap
else return $ Done ctx { stack = VI64 (asWord64 $ truncate v) : rest }
step ctx@EvalCtx{ stack = (VF64 v:rest) } (ITruncFS BS64 BS64) = step ctx@EvalCtx{ stack = (VF64 v:rest) } (ITruncFS BS64 BS64) =
return $ Done ctx { stack = VI64 (asWord64 $ truncate v) : rest } if isNaN v
then return Trap
else return $ Done ctx { stack = VI64 (asWord64 $ truncate v) : rest }
step ctx@EvalCtx{ stack = (VI32 v:rest) } I64ExtendUI32 = step ctx@EvalCtx{ stack = (VI32 v:rest) } I64ExtendUI32 =
return $ Done ctx { stack = VI64 (fromIntegral v) : rest } return $ Done ctx { stack = VI64 (fromIntegral v) : rest }
step ctx@EvalCtx{ stack = (VI32 v:rest) } I64ExtendSI32 = step ctx@EvalCtx{ stack = (VI32 v:rest) } I64ExtendSI32 =
+1 -1
View File
@@ -34,7 +34,7 @@ compile file = do
main :: IO () main :: IO ()
main = do main = do
files <- Directory.listDirectory "tests/samples" files <- Directory.listDirectory "tests/samples"
-- let files = ["data.wast"] -- let files = ["traps.wast"]
scriptTestCases <- (`mapM` files) $ \file -> do scriptTestCases <- (`mapM` files) $ \file -> do
content <- LBS.readFile $ "tests/samples/" ++ file content <- LBS.readFile $ "tests/samples/" ++ file
let Right script = Lexer.scanner content >>= Parser.parseScript let Right script = Lexer.scanner content >>= Parser.parseScript