keep track of defined references and store global function address for table.set instructions
This commit is contained in:
@@ -978,14 +978,14 @@ eval budget store FunctionInstance { funcType, moduleInstance, code = Function {
|
|||||||
let TableInstance { items } = tableInstances store ! tableAddr
|
let TableInstance { items } = tableInstances store ! tableAddr
|
||||||
let dst = fromIntegral offset
|
let dst = fromIntegral offset
|
||||||
let val = case ref of
|
let val = case ref of
|
||||||
RE extRef -> extRef
|
RE extRef -> fromIntegral <$> extRef
|
||||||
RF fnRef -> fnRef
|
RF fnRef -> (funcaddrs moduleInstance !) . fromIntegral <$> fnRef
|
||||||
v -> error "Impossible due to validation"
|
v -> error "Impossible due to validation"
|
||||||
els <- readIORef items
|
els <- readIORef items
|
||||||
if dst >= MVector.length els
|
if dst >= MVector.length els
|
||||||
then return Trap
|
then return Trap
|
||||||
else do
|
else do
|
||||||
MVector.unsafeWrite els dst (fromIntegral <$> val)
|
MVector.unsafeWrite els dst val
|
||||||
return $ Done ctx { stack = rest }
|
return $ Done ctx { stack = rest }
|
||||||
step ctx@EvalCtx{ stack = (VI32 offset:rest) } (TableGet tableIdx) = do
|
step ctx@EvalCtx{ stack = (VI32 offset:rest) } (TableGet tableIdx) = do
|
||||||
let tableAddr = tableaddrs moduleInstance ! fromIntegral tableIdx
|
let tableAddr = tableaddrs moduleInstance ! fromIntegral tableIdx
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ runScript onAssertFail script = do
|
|||||||
getFailureString (Validate.LocalIndexOutOfRange idx) = ["unknown local", "unknown local " <> TL.pack (show idx)]
|
getFailureString (Validate.LocalIndexOutOfRange idx) = ["unknown local", "unknown local " <> TL.pack (show idx)]
|
||||||
getFailureString (Validate.MemoryIndexOutOfRange idx) = ["unknown memory", "unknown memory " <> TL.pack (show idx)]
|
getFailureString (Validate.MemoryIndexOutOfRange idx) = ["unknown memory", "unknown memory " <> TL.pack (show idx)]
|
||||||
getFailureString (Validate.TableIndexOutOfRange idx) = ["unknown table", "unknown table " <> TL.pack (show idx)]
|
getFailureString (Validate.TableIndexOutOfRange idx) = ["unknown table", "unknown table " <> TL.pack (show idx)]
|
||||||
getFailureString Validate.FunctionIndexOutOfRange = ["unknown function", "unknown function 0"]
|
getFailureString (Validate.FunctionIndexOutOfRange idx) = ["unknown function", "unknown function " <> TL.pack (show idx)]
|
||||||
getFailureString (Validate.GlobalIndexOutOfRange idx) = ["unknown global", "unknown global " <> TL.pack (show idx)]
|
getFailureString (Validate.GlobalIndexOutOfRange idx) = ["unknown global", "unknown global " <> TL.pack (show idx)]
|
||||||
getFailureString Validate.LabelIndexOutOfRange = ["unknown label"]
|
getFailureString Validate.LabelIndexOutOfRange = ["unknown label"]
|
||||||
getFailureString Validate.TypeIndexOutOfRange = ["unknown type"]
|
getFailureString Validate.TypeIndexOutOfRange = ["unknown type"]
|
||||||
@@ -194,6 +194,7 @@ runScript onAssertFail script = do
|
|||||||
getFailureString Validate.InvalidStartFunctionType = ["start function"]
|
getFailureString Validate.InvalidStartFunctionType = ["start function"]
|
||||||
getFailureString Validate.InvalidTableType = ["size minimum must not be greater than maximum"]
|
getFailureString Validate.InvalidTableType = ["size minimum must not be greater than maximum"]
|
||||||
getFailureString (Validate.ElemIndexOutOfRange idx) = ["unknown elem segment " <> TL.pack (show idx)]
|
getFailureString (Validate.ElemIndexOutOfRange idx) = ["unknown elem segment " <> TL.pack (show idx)]
|
||||||
|
getFailureString (Validate.UndeclaredFunctionRef _) = ["undeclared function reference"]
|
||||||
getFailureString r = [TL.concat ["not implemented ", TL.pack $ show r]]
|
getFailureString r = [TL.concat ["not implemented ", TL.pack $ show r]]
|
||||||
|
|
||||||
printFailedAssert :: String -> Assertion -> AssertM ()
|
printFailedAssert :: String -> Assertion -> AssertM ()
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ data ValidationError =
|
|||||||
| MemoryLimitExceeded
|
| MemoryLimitExceeded
|
||||||
| AlignmentOverflow
|
| AlignmentOverflow
|
||||||
| MoreThanOneMemory
|
| MoreThanOneMemory
|
||||||
| FunctionIndexOutOfRange
|
| FunctionIndexOutOfRange Natural
|
||||||
| TableIndexOutOfRange Natural
|
| TableIndexOutOfRange Natural
|
||||||
| MemoryIndexOutOfRange Natural
|
| MemoryIndexOutOfRange Natural
|
||||||
| LocalIndexOutOfRange Natural
|
| LocalIndexOutOfRange Natural
|
||||||
@@ -47,6 +47,7 @@ data ValidationError =
|
|||||||
| InvalidConstantExpr
|
| InvalidConstantExpr
|
||||||
| InvalidStartFunctionType
|
| InvalidStartFunctionType
|
||||||
| GlobalIsImmutable
|
| GlobalIsImmutable
|
||||||
|
| UndeclaredFunctionRef Natural
|
||||||
deriving (Show, Eq)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
type ValidationResult = Either ValidationError ()
|
type ValidationResult = Either ValidationError ()
|
||||||
@@ -134,7 +135,8 @@ data Ctx = Ctx {
|
|||||||
locals :: [ValueType],
|
locals :: [ValueType],
|
||||||
labels :: [[ValueType]],
|
labels :: [[ValueType]],
|
||||||
returns :: [ValueType],
|
returns :: [ValueType],
|
||||||
importedGlobals :: Natural
|
importedGlobals :: Natural,
|
||||||
|
refs :: Set.Set Natural
|
||||||
} deriving (Show, Eq)
|
} deriving (Show, Eq)
|
||||||
|
|
||||||
type Checker = ReaderT Ctx (Except ValidationError)
|
type Checker = ReaderT Ctx (Except ValidationError)
|
||||||
@@ -250,7 +252,7 @@ getInstrType Return = do
|
|||||||
return $ (Any : (map Val returns)) ==> Any
|
return $ (Any : (map Val returns)) ==> Any
|
||||||
getInstrType (Call fun) = do
|
getInstrType (Call fun) = do
|
||||||
Ctx { funcs } <- ask
|
Ctx { funcs } <- ask
|
||||||
maybeToEither FunctionIndexOutOfRange $ asArrow <$> funcs !? fun
|
maybeToEither (FunctionIndexOutOfRange fun) $ asArrow <$> funcs !? fun
|
||||||
getInstrType (CallIndirect tableIdx sign) = do
|
getInstrType (CallIndirect tableIdx sign) = do
|
||||||
Ctx { types, tables } <- ask
|
Ctx { types, tables } <- ask
|
||||||
if length tables <= fromIntegral tableIdx
|
if length tables <= fromIntegral tableIdx
|
||||||
@@ -271,10 +273,13 @@ getInstrType RefIsNull = do
|
|||||||
var <- freshVar
|
var <- freshVar
|
||||||
return $ var ==> Val I32
|
return $ var ==> Val I32
|
||||||
getInstrType (RefFunc funIdx) = do
|
getInstrType (RefFunc funIdx) = do
|
||||||
Ctx { funcs } <- ask
|
Ctx { funcs, refs } <- ask
|
||||||
if fromIntegral funIdx < length funcs
|
if fromIntegral funIdx < length funcs
|
||||||
then return $ empty ==> Val Func
|
then do
|
||||||
else throwError FunctionIndexOutOfRange
|
unless (Set.member funIdx refs) $
|
||||||
|
throwError $ UndeclaredFunctionRef $ fromIntegral funIdx
|
||||||
|
return $ empty ==> Val Func
|
||||||
|
else throwError $ FunctionIndexOutOfRange $ fromIntegral funIdx
|
||||||
getInstrType (GetLocal local) = do
|
getInstrType (GetLocal local) = do
|
||||||
Ctx { locals } <- ask
|
Ctx { locals } <- ask
|
||||||
t <- maybeToEither (LocalIndexOutOfRange local) $ locals !? local
|
t <- maybeToEither (LocalIndexOutOfRange local) $ locals !? local
|
||||||
@@ -523,7 +528,7 @@ getFuncTypes Module {types, functions, imports} =
|
|||||||
getFuncType _ = Nothing
|
getFuncType _ = Nothing
|
||||||
|
|
||||||
ctxFromModule :: [ValueType] -> [[ValueType]] -> [ValueType] -> Module -> Ctx
|
ctxFromModule :: [ValueType] -> [[ValueType]] -> [ValueType] -> Module -> Ctx
|
||||||
ctxFromModule locals labels returns m@Module {types, tables, mems, globals, imports, elems} =
|
ctxFromModule locals labels returns m@Module {types, tables, mems, globals, imports, elems, exports} =
|
||||||
let tableImports = catMaybes $ map getTableType imports in
|
let tableImports = catMaybes $ map getTableType imports in
|
||||||
let memsImports = catMaybes $ map getMemType imports in
|
let memsImports = catMaybes $ map getMemType imports in
|
||||||
let globalImports = catMaybes $ map getGlobalType imports in
|
let globalImports = catMaybes $ map getGlobalType imports in
|
||||||
@@ -537,7 +542,10 @@ ctxFromModule locals labels returns m@Module {types, tables, mems, globals, impo
|
|||||||
locals,
|
locals,
|
||||||
labels,
|
labels,
|
||||||
returns,
|
returns,
|
||||||
importedGlobals = fromIntegral $ length globalImports
|
importedGlobals = fromIntegral $ length globalImports,
|
||||||
|
refs = Set.unions $ map getElemRefs elems
|
||||||
|
++ map getGlobalRefs globals
|
||||||
|
++ map getExportRefs exports
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
getTableType (Import _ _ (ImportTable tableType)) = Just tableType
|
getTableType (Import _ _ (ImportTable tableType)) = Just tableType
|
||||||
@@ -549,6 +557,19 @@ ctxFromModule locals labels returns m@Module {types, tables, mems, globals, impo
|
|||||||
getGlobalType (Import _ _ (ImportGlobal gl)) = Just gl
|
getGlobalType (Import _ _ (ImportGlobal gl)) = Just gl
|
||||||
getGlobalType _ = Nothing
|
getGlobalType _ = Nothing
|
||||||
|
|
||||||
|
getElemRefs ElemSegment{ elemType = FuncRef, elements} =
|
||||||
|
foldl extractRef Set.empty elements
|
||||||
|
where
|
||||||
|
extractRef refs [RefFunc idx] = Set.insert idx refs
|
||||||
|
extractRef refs _ = refs
|
||||||
|
getElemRefs _ = Set.empty
|
||||||
|
|
||||||
|
getGlobalRefs Global {initializer = [RefFunc idx]} = Set.singleton idx
|
||||||
|
getGlobalRefs _ = Set.empty
|
||||||
|
|
||||||
|
getExportRefs Export {desc = ExportFunc idx} = Set.singleton idx
|
||||||
|
getExportRefs _ = Set.empty
|
||||||
|
|
||||||
isFunctionValid :: Function -> Validator
|
isFunctionValid :: Function -> Validator
|
||||||
isFunctionValid Function {funcType, localTypes = locals, body} mod@Module {types} =
|
isFunctionValid Function {funcType, localTypes = locals, body} mod@Module {types} =
|
||||||
if fromIntegral funcType < length types
|
if fromIntegral funcType < length types
|
||||||
@@ -664,7 +685,7 @@ startShouldBeValid m@Module { start = Just (StartFunction idx) } =
|
|||||||
let i = fromIntegral idx in
|
let i = fromIntegral idx in
|
||||||
if length types > i
|
if length types > i
|
||||||
then if FuncType [] [] == types !! i then return () else Left InvalidStartFunctionType
|
then if FuncType [] [] == types !! i then return () else Left InvalidStartFunctionType
|
||||||
else Left FunctionIndexOutOfRange
|
else Left $ FunctionIndexOutOfRange $ fromIntegral i
|
||||||
|
|
||||||
exportsShouldBeValid :: Validator
|
exportsShouldBeValid :: Validator
|
||||||
exportsShouldBeValid Module { exports, imports, functions, mems, tables, globals } =
|
exportsShouldBeValid Module { exports, imports, functions, mems, tables, globals } =
|
||||||
@@ -677,7 +698,7 @@ exportsShouldBeValid Module { exports, imports, functions, mems, tables, globals
|
|||||||
|
|
||||||
isExportValid :: Export -> ValidationResult
|
isExportValid :: Export -> ValidationResult
|
||||||
isExportValid (Export _ (ExportFunc funIdx)) =
|
isExportValid (Export _ (ExportFunc funIdx)) =
|
||||||
if fromIntegral funIdx < length funcImports + length functions then return () else Left FunctionIndexOutOfRange
|
if fromIntegral funIdx < length funcImports + length functions then return () else Left (FunctionIndexOutOfRange funIdx)
|
||||||
isExportValid (Export _ (ExportTable tableIdx)) =
|
isExportValid (Export _ (ExportTable tableIdx)) =
|
||||||
if fromIntegral tableIdx < length tableImports + length tables then return () else Left (TableIndexOutOfRange tableIdx)
|
if fromIntegral tableIdx < length tableImports + length tables then return () else Left (TableIndexOutOfRange tableIdx)
|
||||||
isExportValid (Export _ (ExportMemory memIdx)) =
|
isExportValid (Export _ (ExportMemory memIdx)) =
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@ main = do
|
|||||||
files <-
|
files <-
|
||||||
filter (not . List.isPrefixOf "simd") . filter (List.isSuffixOf ".wast")
|
filter (not . List.isPrefixOf "simd") . filter (List.isSuffixOf ".wast")
|
||||||
<$> Directory.listDirectory "tests/spec"
|
<$> Directory.listDirectory "tests/spec"
|
||||||
-- let files = ["table_get.wast"]
|
-- let files = ["ref_func.wast"]
|
||||||
scriptTestCases <- (`mapM` files) $ \file -> do
|
scriptTestCases <- (`mapM` files) $ \file -> do
|
||||||
test <- LBS.readFile ("tests/spec/" ++ file)
|
test <- LBS.readFile ("tests/spec/" ++ file)
|
||||||
return $ testCase file $ do
|
return $ testCase file $ do
|
||||||
|
|||||||
Reference in New Issue
Block a user