From baf856a0d532d02a654ce0666a8cd3b76fa1b1bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20J=C3=A4mthagen?= Date: Thu, 17 Aug 2017 07:24:11 +0200 Subject: [PATCH 01/15] Minor fixes Retrieve latest appended module for validation and break search loop when section is found. --- argparser.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/argparser.py b/argparser.py index a07bbb2..a28f63b 100644 --- a/argparser.py +++ b/argparser.py @@ -605,6 +605,7 @@ def ReadCodeSection(self): if whatever[0] == 10: code_section = whatever.copy() section_exists = True + break if not section_exists: return None @@ -677,6 +678,7 @@ def ReadDataSection(self): if whatever[0] == 11: data_section = whatever.copy() section_exists = True + break if not section_exists: return None @@ -798,6 +800,7 @@ def ReadExportSection(self): if whatever[0] == 7: export_section = whatever.copy() section_exists = True + break if not section_exists: return None @@ -838,6 +841,7 @@ def ReadTypeSection(self): if whatever[0] == 1: type_section = whatever.copy() section_exists = True + break if not section_exists: return None @@ -883,6 +887,7 @@ def ReadFunctionSection(self): if whatever[0] == 3: function_section = whatever.copy() section_exists = True + break if not section_exists: return None @@ -911,6 +916,7 @@ def ReadElementSection(self): if whatever[0] == 9: element_section = whatever.copy() section_exists = True + break if not section_exists: return None @@ -958,6 +964,7 @@ def ReadMemorySection(self): if whatever[0] == 5: memory_section = whatever.copy() section_exists = True + break if not section_exists: return None @@ -1033,6 +1040,7 @@ def ReadGlobalSection(self): if whatever[0] == 6: global_section = whatever.copy() section_exists = True + break if not section_exists: return None @@ -1074,6 +1082,7 @@ def ReadStartSection(self): if whatever[0] == 8: start_section = whatever.copy() section_exists = True + break if not section_exists: return None @@ -1288,7 +1297,7 @@ def dump_sections(self, module): # palceholder for the validation tests def runValidations(self): - modulevalidation = ModuleValidation(self.modules[0]) + modulevalidation = ModuleValidation(self.modules[-1]) return(modulevalidation.ValidateAll()) From 3559c7b4f34e0e940acf1f8f990fabb3646171d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20J=C3=A4mthagen?= Date: Thu, 17 Aug 2017 07:30:47 +0200 Subject: [PATCH 02/15] Add validation for TypeSection * form is required to be func. * Each returns array is required to contain at most one element. --- TBInit.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/TBInit.py b/TBInit.py index bec015d..854bc71 100644 --- a/TBInit.py +++ b/TBInit.py @@ -183,7 +183,13 @@ def __init__(self, module): self.module = module def TypeSection(self): - pass + section = self.module.type_section + if section is None: + return(True) + for func_sig in section.func_types: + if func_sig.form != -0x20 or func_sig.return_cnt > 1: + return(False) + return True def ImportSection(self): pass @@ -219,14 +225,18 @@ def TBCustom(self): pass def ValidateAll(self): - self.TypeSection() + if not self.TypeSection(): + return(False) self.ImportSection() self.FunctionSection() self.TableSection() self.MemorySection() - self.GlobalSection() - self.ExportSection() - self.StartSection() + if not self.GlobalSection(): + return(False) + if not self.ExportSection(): + return(False) + if not self.StartSection(): + return(False) self.ElementSection() self.CodeSection() self.DataSection() From 9cb6ef18e86bf608a826f79a5bf9cd21a9636019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20J=C3=A4mthagen?= Date: Thu, 17 Aug 2017 08:28:22 +0200 Subject: [PATCH 03/15] Add validation for ImportSection * All global imports are required to be immutable. Add classes with constants to improve code readability. --- TBInit.py | 18 +++++++++++++++--- section_structs.py | 14 +++++++++----- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/TBInit.py b/TBInit.py index 854bc71..c3cef25 100644 --- a/TBInit.py +++ b/TBInit.py @@ -1,7 +1,7 @@ from utils import Colors, init_interpret from OpCodes import WASM_OP_Code -from section_structs import Code_Section, Func_Body from execute import * +from section_structs import Code_Section, Func_Body, External_Kind, Global_Kind # handles the debug option --memdump. dumps the contents of linear memories. @@ -192,7 +192,18 @@ def TypeSection(self): return True def ImportSection(self): - pass + section = self.module.import_section + for entry in section.import_entry: + if entry.kind == External_Kind.FUNCTION: + pass + if entry.kind == External_Kind.TABLE: + pass + if entry.kind == External_Kind.MEMORY: + pass + if entry.kind == External_Kind.GLOBAL: + if entry.mutability != Global_Kind.IMMUTABLE: + return(False) + return(True) def FunctionSection(self): pass @@ -227,7 +238,8 @@ def TBCustom(self): def ValidateAll(self): if not self.TypeSection(): return(False) - self.ImportSection() + if not self.ImportSection(): + return(False) self.FunctionSection() self.TableSection() self.MemorySection() diff --git a/section_structs.py b/section_structs.py index ef251c3..a2069a1 100644 --- a/section_structs.py +++ b/section_structs.py @@ -28,11 +28,15 @@ def __init__(self): class External_Kind(): - def __init__(self): - self.Function = 0 - self.Table = 1 - self.Memory = 2 - self.Global = 3 + FUNCTION = 0 + TABLE = 1 + MEMORY = 2 + GLOBAL = 3 + + +class Global_Kind(): + IMMUTABLE = 0 + MUTABLE = 1 class Memory_Type(): From a5811f57309afa152a30c59b44036f044d76f459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20J=C3=A4mthagen?= Date: Thu, 17 Aug 2017 17:10:54 +0200 Subject: [PATCH 04/15] Add validation for ExportSection * Each export's name is required to be unique among all the exports' names. * Each export's index is required to be within the bounds of its associated index space. Also populate module index spaces in Module constructor. --- TBInit.py | 23 ++++++++++++++++++++++- argparser.py | 9 ++++----- section_structs.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/TBInit.py b/TBInit.py index c3cef25..663cecd 100644 --- a/TBInit.py +++ b/TBInit.py @@ -218,7 +218,28 @@ def GlobalSection(self): pass def ExportSection(self): - pass + section = self.module.export_section + name_list = {} + for entry in section.export_entries: + name = ''.join(chr(c) for c in entry.field_str) + if name in name_list: + return(False) + name_list[name]=None + + index = entry.index + if entry.kind == External_Kind.FUNCTION: + if index >= len(self.module.function_index_space): + return(False) + if entry.kind == External_Kind.TABLE: + if index >= len(self.module.table_index_space): + return(False) + if entry.kind == External_Kind.MEMORY: + if index >= len(self.module.memory_index_space): + return(False) + if entry.kind == External_Kind.GLOBAL: + if index >= len(self.module.global_index_space): + return(False) + return(True) def StartSection(self): pass diff --git a/argparser.py b/argparser.py index a28f63b..a8c7f21 100644 --- a/argparser.py +++ b/argparser.py @@ -753,11 +753,11 @@ def ReadImportSection(self): temp_import_entry.kind = kind # function type - if kind == 0: + if kind == External_Kind.FUNCTION: import_type, offset, dummy = Read(import_section[6], offset, 'varuint32') temp_import_entry.type = import_type # table type - elif kind == 1: + elif kind == External_Kind.TABLE: table_type = Table_Type() table_type.elemet_type, offset, dummy = Read(import_section[6], offset, 'varint7') rsz_limits = Resizable_Limits() @@ -767,7 +767,7 @@ def ReadImportSection(self): rsz_limits.maximum, offset, dummy = Read(import_section[6], offset, 'varuint32') table_type.limit = rsz_limits temp_import_entry.type = table_type - elif kind == 2: + elif kind == External_Kind.MEMORY: memory_type = Memory_Type() rsz_limits = Resizable_Limits() rsz_limits.flags, offset, dummy = Read(import_section[6], offset, 'varuint1') @@ -776,7 +776,7 @@ def ReadImportSection(self): rsz_limits.maximum, offset, dummy = Read(import_section[6], offset, 'varuint32') memory_type.limits = rsz_limits temp_import_entry.type = memory_type - elif kind == 3: + elif kind == External_Kind.GLOBAL: global_type = Global_Type() global_type.content_type, offset, dummy = Read(import_section[6], offset, 'uint8') global_type.mutability, offset, dummy = Read(import_section[6], offset, 'varuint1') @@ -1066,7 +1066,6 @@ def ReadGlobalSection(self): temp_gl_var.global_type = temp_gl_type GS.global_variables.append(deepcopy(temp_gl_var)) - count -= 1 loop = True init_expr = [] diff --git a/section_structs.py b/section_structs.py index a2069a1..a3f591c 100644 --- a/section_structs.py +++ b/section_structs.py @@ -228,6 +228,11 @@ class Module(): def __init__(self, type_section, import_section, function_section, table_section, memory_section, global_section, export_section, start_section, element_section, code_section, data_section): + self.function_index_space = [] + self.global_index_space = [] + self.memory_index_space = [] + self.table_index_space = [] + self.type_section = type_section self.import_section = import_section self.function_section = function_section @@ -239,3 +244,27 @@ def __init__(self, type_section, import_section, function_section, self.element_section = element_section self.code_section = code_section self.data_section = data_section + + if self.import_section is not None: + for entry in self.import_section.import_entry: + if entry.kind == External_Kind.FUNCTION: + self.function_index_space.append(entry) + if entry.kind == External_Kind.TABLE: + self.table_index_space.append(entry) + if entry.kind == External_Kind.MEMORY: + self.memory_index_space.append(entry) + if entry.kind == External_Kind.GLOBAL: + self.global_index_space.append(entry) + + for entry in self.function_section.type_section_index: + self.function_index_space.append(entry) + + for entry in self.table_section.table_types: + self.table_index_space.append(entry) + + for entry in self.memory_section.memory_types: + self.memory_index_space.append(entry) + + if self.global_section is not None: + for entry in self.global_section.global_variables: + self.global_index_space.append(entry) From 479d634a299fbf34b4694687eacef3f033bbba04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20J=C3=A4mthagen?= Date: Tue, 29 Aug 2017 14:22:41 +0200 Subject: [PATCH 05/15] Add validation for FunctionSection * The array is required to be the same length as the Code Section array. --- TBInit.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/TBInit.py b/TBInit.py index 663cecd..fd6b710 100644 --- a/TBInit.py +++ b/TBInit.py @@ -206,7 +206,15 @@ def ImportSection(self): return(True) def FunctionSection(self): - pass + section = self.module.function_section + if section is None: + return(True) + csection = self.module.code_section + if csection is None: + return(False) + if len(section.type_section_index) != len(csection.func_bodies): + return(False) + return(True) def TableSection(self): pass @@ -261,7 +269,8 @@ def ValidateAll(self): return(False) if not self.ImportSection(): return(False) - self.FunctionSection() + if not self.FunctionSection(): + return(False) self.TableSection() self.MemorySection() if not self.GlobalSection(): From 065df28f000ce2badd7689c55e7949db668849cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20J=C3=A4mthagen?= Date: Wed, 30 Aug 2017 15:21:00 +0200 Subject: [PATCH 06/15] Add validation for GlobalSection * The type of the value returned by init must be the same as desc's type. --- TBInit.py | 31 ++++++++++++++++++++++++++++++- argparser.py | 2 +- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/TBInit.py b/TBInit.py index fd6b710..2857dd3 100644 --- a/TBInit.py +++ b/TBInit.py @@ -193,6 +193,8 @@ def TypeSection(self): def ImportSection(self): section = self.module.import_section + if section is None: + return(True) for entry in section.import_entry: if entry.kind == External_Kind.FUNCTION: pass @@ -223,10 +225,37 @@ def MemorySection(self): pass def GlobalSection(self): - pass + section = self.module.global_section + if section is None: + return(True) + for entry in section.global_variables: + desc_type = entry.global_type.content_type + init_expr = entry.init_expr + #get_global + if init_expr[0] == 0x23: + index = init_expr[1] + glob = self.module.global_index_space[index] + if desc_type != glob.content_type: + return(False) + #const + elif init_expr[0] == 0x41: + if desc_type != 0x7f: + return(False) + elif init_expr[0] == 0x42: + if desc_type != 0x7e: + return(False) + elif init_expr[0] == 0x43: + if desc_type != 0x7d: + return(False) + elif init_expr[0] == 0x44: + if desc_type != 0x7c: + return(False) + return(True) def ExportSection(self): section = self.module.export_section + if section is None: + return(True) name_list = {} for entry in section.export_entries: name = ''.join(chr(c) for c in entry.field_str) diff --git a/argparser.py b/argparser.py index a8c7f21..9de576a 100644 --- a/argparser.py +++ b/argparser.py @@ -814,7 +814,7 @@ def ReadExportSection(self): for i in range(0, field_length): field_name.append(export_section[6][offset + i]) - temp_export_entry.fiels_str = field_name + temp_export_entry.field_str = field_name offset += field_length kind, offset, dummy = Read(export_section[6], offset, 'uint8') From 2e5e485be2dc4b2f20daefdef2e4db01af5e18a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20J=C3=A4mthagen?= Date: Wed, 30 Aug 2017 16:25:41 +0200 Subject: [PATCH 07/15] Add validation for StartSection * The index is required to be within the bounds of the Code Section array. --- TBInit.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/TBInit.py b/TBInit.py index 2857dd3..779fc3b 100644 --- a/TBInit.py +++ b/TBInit.py @@ -279,7 +279,15 @@ def ExportSection(self): return(True) def StartSection(self): - pass + section = self.module.start_section + if section is None: + return(True) + index = section.function_section_index[0] + csection = self.module.CodeSection + if index >= len(csection.func_bodies): + return(False) + #func = self.module.function_index_space[index] + return(True) def ElementSection(self): pass From 6520409929a124cc5e5a571a476e170f74aef2c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20J=C3=A4mthagen?= Date: Wed, 30 Aug 2017 16:26:19 +0200 Subject: [PATCH 08/15] Make sure table, function and memory sections exist when populating their index spaces --- section_structs.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/section_structs.py b/section_structs.py index a3f591c..f48b60f 100644 --- a/section_structs.py +++ b/section_structs.py @@ -256,14 +256,17 @@ def __init__(self, type_section, import_section, function_section, if entry.kind == External_Kind.GLOBAL: self.global_index_space.append(entry) - for entry in self.function_section.type_section_index: - self.function_index_space.append(entry) + if self.function_section is not None: + for entry in self.function_section.type_section_index: + self.function_index_space.append(entry) - for entry in self.table_section.table_types: - self.table_index_space.append(entry) + if self.table_section is not None: + for entry in self.table_section.table_types: + self.table_index_space.append(entry) - for entry in self.memory_section.memory_types: - self.memory_index_space.append(entry) + if self.memory_section is not None: + for entry in self.memory_section.memory_types: + self.memory_index_space.append(entry) if self.global_section is not None: for entry in self.global_section.global_variables: From d96d216477b6e1daa1e25c40ca54322fc4f95acb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20J=C3=A4mthagen?= Date: Thu, 31 Aug 2017 09:54:42 +0200 Subject: [PATCH 09/15] Test: Move section validation tests to its own file --- test/sectionvalidation.py | 63 +++++++++++++++++++++++++++++++++++++++ test/test.py | 48 +++++++---------------------- 2 files changed, 74 insertions(+), 37 deletions(-) create mode 100644 test/sectionvalidation.py diff --git a/test/sectionvalidation.py b/test/sectionvalidation.py new file mode 100644 index 0000000..53d5fce --- /dev/null +++ b/test/sectionvalidation.py @@ -0,0 +1,63 @@ +import sys +import os +sys.path.append('../') +from utils import Colors +from argparser import * +from TBInit import * + +success = Colors.green + "SUCCESS: " + Colors.ENDC +fail = Colors.red + "FAIL: " + Colors.ENDC + +def ObjectList(): + obj_list = [] + cwd = os.getcwd() + for file in os.listdir(cwd + "/testsuite"): + if file.endswith(".wasm"): + obj_list.append(cwd + "/testsuite/" + file) + + return(obj_list) + +def section_validation(): + return_list = [] + obj_list = ObjectList() + for testfile in obj_list: + pid = os.fork() + # I dont have a bellybutton + if pid == 0: + # @DEVI-FIXME-pipe stdout and stderr to a file instead of the + # bitbucket + sys.stdout = open('/dev/null', 'w') + sys.stderr = open('/dev/null', 'w') + + interpreter = PythonInterpreter() + module = interpreter.parse(testfile) + interpreter.appendmodule(module) + interpreter.dump_sections(module) + interpreter.runValidations() + vm = VM(interpreter.getmodules()) + ms = vm.getState() + # interpreter.dump_sections(module) + DumpIndexSpaces(ms) + DumpLinearMems(ms.Linear_Memory, 1000) + sys.exit() + # the parent process + elif pid > 0: + # @DEVI-FIXME-we are intentionally blocking. later i will fix this + # so we can use multicores to run our reg tests faster. + cpid, status = os.waitpid(pid, 0) + return_list.append(status) + if status == 0: + print(success + testfile) + else: + print(fail + testfile) + else: + # basically we couldnt fork a child + print(fail + 'return code:' + pid) + raise Exception("could not fork child") + +def main(): + sectionvalidation(False) + sectionvalidation(True) + +if __name__ == '__main__': + main() diff --git a/test/test.py b/test/test.py index 1a5546f..7024963 100755 --- a/test/test.py +++ b/test/test.py @@ -10,6 +10,7 @@ from test_LEB128 import test_unsigned_LEB128 from leb128s import leb128sencodedecodeexhaustive from leb128s import leb128uencodedecodeexhaustive +from sectionvalidation import * from abc import ABCMeta, abstractmethod sys.path.append('../') from utils import Colors @@ -88,52 +89,25 @@ def Legacy(self): def GetName(self): return('leb128exhaustive') +class SectionValidationTest(Void_Spwner): + def Legacy(self): + section_validation() + + def GetName(self): + return('sectionvalidationtest') + ################################################################################ def main(): - return_list = [] # LEB128 tests leb128encodetest = LEB128EncodeTest() leb128encodetest.Spwn() # leb128s exhaustive leb128sex = LEB128Exhaustive() leb128sex.Spwn() - # parser test on the WASM testsuite - obj_list = ObjectList() - for testfile in obj_list: - pid = os.fork() - # I dont have a bellybutton - if pid == 0: - # @DEVI-FIXME-pipe stdout and stderr to a file instead of the - # bitbucket - sys.stdout = open('/dev/null', 'w') - sys.stderr = open('/dev/null', 'w') - - interpreter = PythonInterpreter() - module = interpreter.parse(testfile) - interpreter.appendmodule(module) - interpreter.dump_sections(module) - interpreter.runValidations() - vm = VM(interpreter.getmodules()) - ms = vm.getState() - # interpreter.dump_sections(module) - DumpIndexSpaces(ms) - DumpLinearMems(ms.Linear_Memory, 1000) - sys.exit() - # the parent process - elif pid > 0: - # @DEVI-FIXME-we are intentionally blocking. later i will fix this - # so we can use multicores to run our reg tests faster. - cpid, status = os.waitpid(pid, 0) - return_list.append(status) - if status == 0: - print(success + testfile) - else: - print(fail + testfile) - else: - # basically we couldnt fork a child - print(fail + 'return code:' + pid) - raise Exception("could not fork child") + # parser test on the WASM testsuite + sectionvalidation = SectionValidationTest() + sectionvalidation.Spwn() if __name__ == '__main__': main() From 535d6f93716cab3040b872719f7923f671ecd24d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20J=C3=A4mthagen?= Date: Thu, 31 Aug 2017 11:29:50 +0200 Subject: [PATCH 10/15] bugfix: faulty indentation --- utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils.py b/utils.py index cbbde25..254a978 100644 --- a/utils.py +++ b/utils.py @@ -153,8 +153,8 @@ def Read(section_byte, offset, kind): # we have read the lasy byte of the operand break - return_list = LEB128SignedDecode(operand) - operand = [] + return_list = LEB128SignedDecode(operand) + operand = [] return return_list, offset, read_bytes From 132b3df3a7117a56b2b2a1cb943d068c38db39c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20J=C3=A4mthagen?= Date: Thu, 31 Aug 2017 14:27:45 +0200 Subject: [PATCH 11/15] Test: Make sure type section validation fails correctly This commit adds tests and invalid wasm testfiles that checks the type section. The file invalid_form.wasm is based on testsuite/address.wasm and was edited to have the first entry of the type section include an invalid form (0x60 => 0x61). The file invalid_retcount.wasm is also based on testsuite/address.wasm and was edited to have the first type section entry include two return values instead of one. --- test/sectionvalidation.py | 45 ++++++++++++++++-- test/test.py | 22 +++++---- test/testsuite_fail/type/invalid_form.wasm | Bin 0 -> 393 bytes .../testsuite_fail/type/invalid_retcount.wasm | Bin 0 -> 394 bytes 4 files changed, 53 insertions(+), 14 deletions(-) create mode 100644 test/testsuite_fail/type/invalid_form.wasm create mode 100644 test/testsuite_fail/type/invalid_retcount.wasm diff --git a/test/sectionvalidation.py b/test/sectionvalidation.py index 53d5fce..632f4e5 100644 --- a/test/sectionvalidation.py +++ b/test/sectionvalidation.py @@ -8,18 +8,55 @@ success = Colors.green + "SUCCESS: " + Colors.ENDC fail = Colors.red + "FAIL: " + Colors.ENDC -def ObjectList(): +def ObjectList(directory): obj_list = [] cwd = os.getcwd() - for file in os.listdir(cwd + "/testsuite"): + + for file in os.listdir(cwd + directory): if file.endswith(".wasm"): - obj_list.append(cwd + "/testsuite/" + file) + obj_list.append(cwd + directory + "/" + file) return(obj_list) +def section_validation_fail(directory): + return_list = [] + obj_list = ObjectList(directory) + for testfile in obj_list: + pid = os.fork() + # I dont have a bellybutton + if pid == 0: + # @DEVI-FIXME-pipe stdout and stderr to a file instead of the + # bitbucket + sys.stdout = open('/dev/null', 'w') + sys.stderr = open('/dev/null', 'w') + + interpreter = PythonInterpreter() + module = interpreter.parse(testfile) + interpreter.appendmodule(module) + interpreter.dump_sections(module) + if "type" in directory: + if not interpreter.TypeSection(): + sys.exit(1) + sys.exit() + # the parent process + elif pid > 0: + # @DEVI-FIXME-we are intentionally blocking. later i will fix this + # so we can use multicores to run our reg tests faster. + cpid, status = os.waitpid(pid, 0) + return_list.append(status) + if status != 0: + print(success + testfile) + else: + print(fail + testfile) + else: + # basically we couldnt fork a child + print(fail + 'return code:' + pid) + raise Exception("could not fork child") + + def section_validation(): return_list = [] - obj_list = ObjectList() + obj_list = ObjectList("/testsuite") for testfile in obj_list: pid = os.fork() # I dont have a bellybutton diff --git a/test/test.py b/test/test.py index 7024963..376db61 100755 --- a/test/test.py +++ b/test/test.py @@ -62,16 +62,6 @@ def Spwn(self): print(fail + 'return code:' + pid) raise Exception("could not fork child") - -def ObjectList(): - obj_list = [] - cwd = os.getcwd() - for file in os.listdir(cwd + "/testsuite"): - if file.endswith(".wasm"): - obj_list.append(cwd + "/testsuite/" + file) - - return(obj_list) - ################################################################################ class LEB128EncodeTest(Void_Spwner): def Legacy(self): @@ -91,7 +81,19 @@ def GetName(self): class SectionValidationTest(Void_Spwner): def Legacy(self): + fail_dir = "/testsuite_fail/" section_validation() + section_validation_fail(fail_dir + "type") + #section_validation_fail(fail_dir + "import") + #section_validation_fail(fail_dir + "function") + #section_validation_fail(fail_dir + "table") + #section_validation_fail(fail_dir + "memory") + #section_validation_fail(fail_dir + "global") + #section_validation_fail(fail_dir + "export") + #section_validation_fail(fail_dir + "start") + #section_validation_fail(fail_dir + "element") + #section_validation_fail(fail_dir + "code") + #section_validation_fail(fail_dir + "data") def GetName(self): return('sectionvalidationtest') diff --git a/test/testsuite_fail/type/invalid_form.wasm b/test/testsuite_fail/type/invalid_form.wasm new file mode 100644 index 0000000000000000000000000000000000000000..56ebe0d688094712a0deb7bda9ef206733f2e5f7 GIT binary patch literal 393 zcmYk0O;W-@5QSe)2q=TNhLy|NSi_$ROK)Nzh#(mG72G5i3#%-uyaFfiavT6M9gCUX zd~dqn>uCh@2?3y=BuO9wiYb1g7~ZdWm(z(Y^0%f*pu&Nt{ve9NI>2mzm`w;~3m)?h zIp#fl<^%G~HVVuRF7mvmhRaOUQOraGG80XdylxQUs`&S1&iXdr;|8~Yw3-|(99=nU zP1diStcJ~MvRMtARl;VK<5!MfrT*RjZAu)|x~@vOHyy$wWHspa!v6E%WjJ~rPu`;U N>1@7Ou0GbA&lCI+T&e&7 literal 0 HcmV?d00001 diff --git a/test/testsuite_fail/type/invalid_retcount.wasm b/test/testsuite_fail/type/invalid_retcount.wasm new file mode 100644 index 0000000000000000000000000000000000000000..82894769ac7bfb510a0405371a443537a5d2d9a3 GIT binary patch literal 394 zcmYk0O-=$q5QSg$IDj<58dfZOLt1`RSaK5vM^O=mf5FX+aY15CxPm9(avT7sYmD8s z=xyEHCnkZ(X1(}I9O5QLCaaBBin6vigd(_bYq}AkT;ppnW z)@1$K$!gfFCY#l;StV>%Iez8%RqFr#yG@C6T9ulVKc++IL)L=fefaP=dU}2tzfPvn OY`$2o)| Date: Fri, 1 Sep 2017 07:55:55 +0200 Subject: [PATCH 12/15] Test: Make sure global section validation fails correctly The file incompatible_init_desc_type.wasm is based on the file testsuite/globals.wasm and has the first global declaration in the global section edited to change the global description type from 0x7f to 0x7e, thus making it incompatible with the type returned from the instantiation-time initializer. --- test/sectionvalidation.py | 3 +++ test/test.py | 2 +- .../global/incompatible_init_desc_type.wasm | Bin 0 -> 359 bytes 3 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 test/testsuite_fail/global/incompatible_init_desc_type.wasm diff --git a/test/sectionvalidation.py b/test/sectionvalidation.py index 632f4e5..c94f01b 100644 --- a/test/sectionvalidation.py +++ b/test/sectionvalidation.py @@ -37,6 +37,9 @@ def section_validation_fail(directory): if "type" in directory: if not interpreter.TypeSection(): sys.exit(1) + if "global" in directory: + if not interpreter.GlobalSection(): + sys.exit(1) sys.exit() # the parent process elif pid > 0: diff --git a/test/test.py b/test/test.py index 376db61..37f9d85 100755 --- a/test/test.py +++ b/test/test.py @@ -88,7 +88,7 @@ def Legacy(self): #section_validation_fail(fail_dir + "function") #section_validation_fail(fail_dir + "table") #section_validation_fail(fail_dir + "memory") - #section_validation_fail(fail_dir + "global") + section_validation_fail(fail_dir + "global") #section_validation_fail(fail_dir + "export") #section_validation_fail(fail_dir + "start") #section_validation_fail(fail_dir + "element") diff --git a/test/testsuite_fail/global/incompatible_init_desc_type.wasm b/test/testsuite_fail/global/incompatible_init_desc_type.wasm new file mode 100644 index 0000000000000000000000000000000000000000..f37b912d2850b7c65a787c0835e52eaf1cd0abd2 GIT binary patch literal 359 zcmYL?JC4FI5QhIr`~YSPj(`Lu6d*ypJPyDS0urL4Ku~~4oD_&|53vVX368=c=yw=l z#nSlu?0-DQ>aZgK^jlRG8X2gR3K^BiC_ucB$}=kDoFn~tw|#*?NouUI&bvQ-888i8 zU=DoWImbd~%4**^V0Ph>rbjMF(&dA5`lNkHX|`wo)Tws6ISy8Uat%_RkSNbc?LqPr zjPeYt_d_`42wpixLpecH`^IP~C*amgzq!75bip4b%7 literal 0 HcmV?d00001 From 3a539a357a78d6e9696a06ff27d7a8224cbb8c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20J=C3=A4mthagen?= Date: Fri, 1 Sep 2017 11:06:40 +0200 Subject: [PATCH 13/15] Bugfix: Do proper validation testing of the module Also make sure invalid_retcount.wasm test is properly formatted so parser doesn't crash. --- test/sectionvalidation.py | 8 +++++--- test/testsuite_fail/type/invalid_retcount.wasm | Bin 394 -> 393 bytes 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/test/sectionvalidation.py b/test/sectionvalidation.py index c94f01b..8f919e8 100644 --- a/test/sectionvalidation.py +++ b/test/sectionvalidation.py @@ -34,11 +34,12 @@ def section_validation_fail(directory): module = interpreter.parse(testfile) interpreter.appendmodule(module) interpreter.dump_sections(module) + modulevalidation = ModuleValidation(module) if "type" in directory: - if not interpreter.TypeSection(): + if not modulevalidation.TypeSection(): sys.exit(1) if "global" in directory: - if not interpreter.GlobalSection(): + if not modulevalidation.GlobalSection(): sys.exit(1) sys.exit() # the parent process @@ -73,7 +74,8 @@ def section_validation(): module = interpreter.parse(testfile) interpreter.appendmodule(module) interpreter.dump_sections(module) - interpreter.runValidations() + if not interpreter.runValidations(): + sys.exit(1) vm = VM(interpreter.getmodules()) ms = vm.getState() # interpreter.dump_sections(module) diff --git a/test/testsuite_fail/type/invalid_retcount.wasm b/test/testsuite_fail/type/invalid_retcount.wasm index 82894769ac7bfb510a0405371a443537a5d2d9a3..c31bbac3b60a475a8948fb13d1a1ab7373a2d91a 100644 GIT binary patch delta 12 TcmeBT?qr@I#>lWyqKFXy6)OV$ delta 13 UcmeBV?qZ%G#>7~^QM`x|02yxseE Date: Fri, 1 Sep 2017 15:33:25 +0200 Subject: [PATCH 14/15] Add skeleton code for test coverage of section validation fails --- test/sectionvalidation.py | 27 +++++++++++++++++++++++++++ test/test.py | 18 +++++++++--------- test/testsuite_fail/README.md | 1 + 3 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 test/testsuite_fail/README.md diff --git a/test/sectionvalidation.py b/test/sectionvalidation.py index 8f919e8..a1c87b5 100644 --- a/test/sectionvalidation.py +++ b/test/sectionvalidation.py @@ -38,9 +38,36 @@ def section_validation_fail(directory): if "type" in directory: if not modulevalidation.TypeSection(): sys.exit(1) + if "import" in directory: + if not modulevalidation.ImportSection(): + sys.exit(1) + if "function" in directory: + if not modulevalidation.FunctionSection(): + sys.exit(1) + if "table" in directory: + if not modulevalidation.TableSection(): + sys.exit(1) + if "memory" in directory: + if not modulevalidation.MemorySection(): + sys.exit(1) if "global" in directory: if not modulevalidation.GlobalSection(): sys.exit(1) + if "export" in directory: + if not modulevalidation.ExportSection(): + sys.exit(1) + if "start" in directory: + if not modulevalidation.StartSection(): + sys.exit(1) + if "element" in directory: + if not modulevalidation.ElementSection(): + sys.exit(1) + if "code" in directory: + if not modulevalidation.CodeSection(): + sys.exit(1) + if "data" in directory: + if not modulevalidation.DataSection(): + sys.exit(1) sys.exit() # the parent process elif pid > 0: diff --git a/test/test.py b/test/test.py index 37f9d85..efbc836 100755 --- a/test/test.py +++ b/test/test.py @@ -84,16 +84,16 @@ def Legacy(self): fail_dir = "/testsuite_fail/" section_validation() section_validation_fail(fail_dir + "type") - #section_validation_fail(fail_dir + "import") - #section_validation_fail(fail_dir + "function") - #section_validation_fail(fail_dir + "table") - #section_validation_fail(fail_dir + "memory") + section_validation_fail(fail_dir + "import") + section_validation_fail(fail_dir + "function") + section_validation_fail(fail_dir + "table") + section_validation_fail(fail_dir + "memory") section_validation_fail(fail_dir + "global") - #section_validation_fail(fail_dir + "export") - #section_validation_fail(fail_dir + "start") - #section_validation_fail(fail_dir + "element") - #section_validation_fail(fail_dir + "code") - #section_validation_fail(fail_dir + "data") + section_validation_fail(fail_dir + "export") + section_validation_fail(fail_dir + "start") + section_validation_fail(fail_dir + "element") + section_validation_fail(fail_dir + "code") + section_validation_fail(fail_dir + "data") def GetName(self): return('sectionvalidationtest') diff --git a/test/testsuite_fail/README.md b/test/testsuite_fail/README.md new file mode 100644 index 0000000..3bf4fd5 --- /dev/null +++ b/test/testsuite_fail/README.md @@ -0,0 +1 @@ +This directory will contain invalid wasm files aimed at testing section validation and that they fail in a correct manner. From 347f65d4e7923db7bf6422b8180209436c3cf23a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20J=C3=A4mthagen?= Date: Wed, 6 Sep 2017 13:28:56 +0200 Subject: [PATCH 15/15] Utilize existing code for module index spaces --- TBInit.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/TBInit.py b/TBInit.py index 779fc3b..e14098f 100644 --- a/TBInit.py +++ b/TBInit.py @@ -252,7 +252,7 @@ def GlobalSection(self): return(False) return(True) - def ExportSection(self): + def ExportSection(self, machinestate): section = self.module.export_section if section is None: return(True) @@ -265,16 +265,16 @@ def ExportSection(self): index = entry.index if entry.kind == External_Kind.FUNCTION: - if index >= len(self.module.function_index_space): + if index >= len(machinestate.Index_Space_Function): return(False) if entry.kind == External_Kind.TABLE: - if index >= len(self.module.table_index_space): + if index >= len(machinestate.Index_Space_Table): return(False) if entry.kind == External_Kind.MEMORY: - if index >= len(self.module.memory_index_space): + if index >= len(machinestate.Index_Space_Linear): return(False) if entry.kind == External_Kind.GLOBAL: - if index >= len(self.module.global_index_space): + if index >= len(machinestate.Index_Space_Global): return(False) return(True) @@ -302,6 +302,10 @@ def TBCustom(self): pass def ValidateAll(self): + machinestate = TBMachine() + init = TBInit(self.module, machinestate) + init.run() + machinestate = init.getInits() if not self.TypeSection(): return(False) if not self.ImportSection(): @@ -312,7 +316,7 @@ def ValidateAll(self): self.MemorySection() if not self.GlobalSection(): return(False) - if not self.ExportSection(): + if not self.ExportSection(machinestate): return(False) if not self.StartSection(): return(False)