Exemplo n.º 1
0
    def validate_arg(self, arg):
        if self.is_integer_type:
            if self is self.i64:
                lower, upper = (0, constants.UINT64_MAX)
                type_ = numpy.uint64
            elif self is self.i32:
                lower, upper = (0, constants.UINT32_MAX)
                type_ = numpy.uint32
            else:
                raise Exception("Invariant")

            if not isinstance(arg, type_) or arg < lower or arg > upper:
                raise ValidationError(
                    f"Invalid argument for {self.value}: {arg}")
        elif self.is_float_type:
            if self is self.f64:
                lower, upper = (constants.FLOAT64_MIN, constants.FLOAT64_MAX)
                type_ = numpy.float64
            elif self is self.f32:
                lower, upper = (constants.FLOAT32_MIN, constants.FLOAT32_MAX)
                type_ = numpy.float32
            else:
                raise Exception("Invariant")

            if not isinstance(arg, type_):
                raise ValidationError(
                    f"Invalid argument for {self.value}: {arg}")

            if numpy.isnan(arg) or numpy.isinf(arg):
                pass
            elif arg < lower or arg > upper:
                raise ValidationError(
                    f"Invalid argument for {self.value}: {arg}")
        else:
            raise Exception("Invariant")
Exemplo n.º 2
0
def validate_element_segment(context: Context,
                             element_segment: ElementSegment) -> None:
    context.validate_table_idx(element_segment.table_idx)
    table_type = context.get_table(element_segment.table_idx)

    elem_type = table_type.elem_type
    if elem_type is not FunctionAddress:
        raise ValidationError(
            f"Table has invalid element type.  Expected `FunctionAddress`. "
            f"Got: `{elem_type}`")

    expected_result_type = (ValType.i32, )
    with context.expression_context() as ctx:
        expression = Block.wrap(expected_result_type, element_segment.offset)
        validate_expression(expression, ctx)

    result_type = ctx.result_type

    if result_type != expected_result_type:
        raise ValidationError(
            f"Invalid data segment.  Return type must be '(i32,)'.  Got "
            f"{result_type}")

    with context.expression_context() as ctx:
        validate_constant_expression(element_segment.offset, ctx)

    for function_idx in element_segment.init:
        context.validate_function_idx(function_idx)
Exemplo n.º 3
0
def validate_external_type_match(external_type_a, external_type_b):
    """
    Validate the Extern types.
    """
    if type(external_type_a) is not type(external_type_b):
        raise ValidationError(
            f"Mismatch in extern types: {type(external_type_a)} != "
            f"{type(external_type_b)}"
        )
    elif isinstance(external_type_a, FunctionType):
        if external_type_a != external_type_b:
            raise ValidationError(
                f"Function types not equal: {external_type_a} != {external_type_b}"
            )
    elif isinstance(external_type_a, TableType):
        validate_limits_match(external_type_a.limits, external_type_b.limits)

        if external_type_a.elem_type is not external_type_b.elem_type:
            raise ValidationError(
                f"Table element type mismatch: {external_type_a.elem_type} != "
                f"{external_type_b.elem_type}"
            )
    elif isinstance(external_type_a, MemoryType):
        validate_limits_match(external_type_a, external_type_b)
    elif isinstance(external_type_a, GlobalType):
        if external_type_a != external_type_b:
            raise ValidationError(
                f"Globals extern type mismatch: {external_type_a} != {external_type_b}"
            )
    else:
        raise Exception(f"Invariant: unknown extern type: {type(external_type_a)}")
Exemplo n.º 4
0
def validate_limits_match(limits_a: TLimits, limits_b: TLimits) -> None:
    if limits_a.min < limits_b.min:
        raise ValidationError(
            f"Limits.min mismatch: {limits_a.min} != {limits_b.min}")
    elif limits_b.max is None:
        return
    elif limits_a.max is not None and limits_a.max <= limits_b.max:
        return
    else:
        raise ValidationError(
            f"Limits.max mismatch: {limits_a.max} != {limits_b.max}")
Exemplo n.º 5
0
def get_import_type(module: Module, descriptor: TImportDesc) -> TExtern:
    if isinstance(descriptor, TypeIdx):
        if descriptor >= len(module.types):
            raise ValidationError(
                f"Invalid import descriptor.  Type index is out of range. "
                f"type_idx={descriptor} > {len(module.types)}")
        return module.types[descriptor]
    elif isinstance(descriptor, (TableType, MemoryType, GlobalType)):
        return descriptor
    else:
        raise ValidationError(
            f"Unknown import descriptor type: {type(descriptor)}")
Exemplo n.º 6
0
def validate_limits_match(limits_a: TLimits, limits_b: TLimits) -> None:
    """
    Validate that two Limits objects are compatible as part of Extern type validation
    """
    if limits_a.min < limits_b.min:
        raise ValidationError(f"Limits.min mismatch: {limits_a.min} != {limits_b.min}")
    elif limits_b.max is None:
        return
    elif limits_a.max is not None and limits_a.max <= limits_b.max:
        return
    else:
        raise ValidationError(f"Limits.max mismatch: {limits_a.max} != {limits_b.max}")
Exemplo n.º 7
0
    def pop_control_frame(self) -> ControlFrame:
        try:
            frame = self.control_stack.peek()
        except IndexError:
            raise ValidationError("Attempt to pop from empty control stack")

        self.pop_operands_of_expected_types(frame.end_types)

        if len(self.operand_stack) != frame.height:
            raise ValidationError(
                f"Operand stack height invalid.  Expected: {frame.height}  Got: "
                f"{len(self.operand_stack)}")
        return self.control_stack.pop()
Exemplo n.º 8
0
    def grow(self, num_pages: numpy.uint32) -> numpy.uint32:
        new_num_pages = num_pages + len(self.data) // constants.PAGE_SIZE_64K
        if new_num_pages >= constants.UINT16_CEIL:
            raise ValidationError(
                f"Memory length exceeds u16 bounds: {new_num_pages} > {constants.UINT16_CEIL}"
            )
        elif self.max is not None and self.max < new_num_pages:
            raise ValidationError(
                f"Memory length exceeds maximum memory size bounds: {new_num_pages} > "
                f"{self.max}")

        self.data.extend(bytearray(num_pages * constants.PAGE_SIZE_64K))
        self._length_cache = len(self.data)
        return numpy.uint32(new_num_pages)
Exemplo n.º 9
0
 def _decomission(self) -> None:
     if self.is_final:
         raise ValidationError(
             "This `ExpressionContext` has already been finalized.")
     self._result_type = tuple(self.operand_stack)
     self._operand_stack = None
     self._control_stack = None
     self.is_final = True
Exemplo n.º 10
0
def validate_constant_instruction(instruction: BaseInstruction,
                                  context: Context) -> None:
    if instruction.opcode is BinaryOpcode.GET_GLOBAL:
        global_ = context.get_global(cast(GlobalOp, instruction).global_idx)

        if global_.mut is not Mutability.const:
            raise ValidationError(
                "Attempt to access mutable global variable within constant "
                "expression")
        validate_get_global(cast(GlobalOp, instruction), context)
    elif instruction.opcode.is_numeric_constant:
        validate_numeric_constant(cast(TNumericConstant, instruction), context)
    else:
        raise ValidationError(
            "Illegal instruction.  Only "
            "I32_CONST/I64_CONST/F32_CONST/F64_CONST/GET_GLOBAL are allowed. "
            f"Got {instruction.opcode}")
Exemplo n.º 11
0
def validate_function_type(function_type: FunctionType) -> None:
    """
    Validate a FunctionType object.
    """
    if len(function_type.results) > 1:
        raise ValidationError(
            f"Function types may only have one result.  Got {len(function_type.results)}"
        )
Exemplo n.º 12
0
def validate_start_function(context: Context, start: StartFunction) -> None:
    context.validate_function_idx(start.function_idx)
    function_type = context.get_function(start.function_idx)

    if function_type != FunctionType((), ()):
        raise ValidationError(
            "Start function may not have arguments or a result type.  Got "
            f"{function_type}")
Exemplo n.º 13
0
def validate_memory_store(instruction: MemoryOp, context: Context) -> None:
    context.validate_mem_idx(MemoryIdx(0))

    align_ceil = instruction.memory_bit_size.value // 8
    if 2**instruction.memarg.align > align_ceil:
        raise ValidationError("Invalid memarg alignment")

    context.pop_operand_and_assert_type(instruction.valtype)
    context.pop_operand_and_assert_type(ValType.i32)
Exemplo n.º 14
0
def validate_table_type(table_type: TableType) -> None:
    """
    https://webassembly.github.io/spec/core/bikeshed/index.html#table-types%E2%91%A2
    """
    validate_limits(table_type.limits, constants.UINT32_CEIL)
    if table_type.elem_type is not FunctionAddress:
        raise ValidationError(
            f"TableType.elem_type must be `FunctionAddress`: Got {table_type.elem_type}"
        )
Exemplo n.º 15
0
 def __enter__(self) -> 'ExpressionContext':
     """
     Enter the validation context for expression validation.
     """
     if self.is_final:
         raise ValidationError(
             "This `ExpressionContext` has already been finalized."
         )
     return self
Exemplo n.º 16
0
def validate_version(version: TVersion) -> None:
    """
    Validate the parsed version string for a Web Assembly module.
    """
    if version != constants.VERSION_1:
        raise ValidationError(
            f"Unknown version. Got: "
            f"{tuple(hex(byte) for byte in version)}"
        )
Exemplo n.º 17
0
def validate_function_types(module: Module) -> None:
    # This validation is explicitly in the spec but it gives us strong
    # guarantees about indexing into the module types to populate the function
    # types.
    for function in module.funcs:
        if function.type_idx >= len(module.types):
            raise ValidationError(
                f"Function type index is out of range. "
                f"type_idx={function.type_idx} > {len(module.types)}")
Exemplo n.º 18
0
def validate_set_global(instruction: GlobalOp, ctx: ExpressionContext) -> None:
    ctx.validate_global_idx(instruction.global_idx)
    global_ = ctx.get_global(instruction.global_idx)

    if global_.mut is not Mutability.var:  # type: ignore
        raise ValidationError(
            f"Global variable at index {instruction.global_idx} is immutable "
            "and cannot be modified")

    ctx.pop_operand_and_assert_type(global_.valtype)
Exemplo n.º 19
0
    def pop_operand(self) -> Operand:
        frame = self.control_stack.peek()

        if frame.is_unreachable and len(self.operand_stack) == frame.height:
            return Unknown
        elif len(self.operand_stack) <= frame.height:
            raise ValidationError(
                f"Underflow: Insufficient operands: {len(self.operand_stack)} <= "
                f"{frame.height}")
        else:
            return self.operand_stack.pop()
Exemplo n.º 20
0
    def pop_operand_and_assert_type(self, expected: Operand) -> Operand:
        actual = self.pop_operand()

        if actual is Unknown or expected is Unknown:
            return expected
        elif actual == expected:
            return actual
        else:
            raise ValidationError(
                f"Type mismatch on operand stack.  Expected: {expected}  Got: "
                f"{actual}")
Exemplo n.º 21
0
def get_export_type(context: Context, descriptor: TExportDesc) -> TExtern:
    if isinstance(descriptor, FunctionIdx):
        return context.get_function(descriptor)
    elif isinstance(descriptor, TableIdx):
        return context.get_table(descriptor)
    elif isinstance(descriptor, MemoryIdx):
        return context.get_mem(descriptor)
    elif isinstance(descriptor, GlobalIdx):
        return context.get_global(descriptor)
    else:
        raise ValidationError(
            f"Unknown export descriptor type: {type(descriptor)}")
Exemplo n.º 22
0
def validate_limits(limits: Limits, upper_bound: int) -> None:
    """
    Validate a Limits object

    https://webassembly.github.io/spec/core/bikeshed/index.html#limits%E2%91%A2
    """
    if limits.min > constants.UINT32_MAX:
        raise ValidationError(
            "Limits.min is outside of u32 range: Got {limits.min}")
    elif limits.min < 0:
        raise ValidationError("Limits.min is negative: Got {limits.min}")
    elif limits.min > upper_bound:
        raise ValidationError(
            f"Limits.min exceeds upper bound: {limits.min} > {upper_bound}")
    elif limits.max is not None:
        if limits.max > constants.UINT32_MAX:
            raise ValidationError(
                "Limits.max is outside of u32 range: Got {limits.max}")
        elif limits.max > upper_bound:
            raise ValidationError(
                f"Limits.max exceeds upper bound: {limits.max} > {upper_bound}"
            )
        elif limits.min > limits.max:
            raise ValidationError(
                f"Limits.min exceeds Limits.max: {limits.min} > "
                f"{limits.max}")
Exemplo n.º 23
0
def validate_import_descriptor(context: Context,
                               descriptor: TImportDesc) -> None:
    if isinstance(descriptor, TypeIdx):
        context.validate_type_idx(descriptor)
    elif isinstance(descriptor, TableType):
        validate_table_type(descriptor)
    elif isinstance(descriptor, MemoryType):
        validate_memory_type(descriptor)
    elif isinstance(descriptor, GlobalType):
        pass
    else:
        raise ValidationError(
            f"Unknown import descriptor type: {type(descriptor)}")
Exemplo n.º 24
0
 def _decomission(self) -> None:
     """
     Triggered once the validation context exits, ensuring that no
     accidental mutation may occur outside of that context.
     """
     if self.is_final:
         raise ValidationError(
             "This `ExpressionContext` has already been finalized."
         )
     self._result_type = tuple(self.operand_stack)
     self._operand_stack = None
     self._control_stack = None
     self.is_final = True
Exemplo n.º 25
0
def validate_memory_store(instruction: MemoryOp,
                          ctx: ExpressionContext) -> None:
    """
    Validate one of the STORE memory instruction as part of expression validation
    """
    ctx.validate_mem_idx(MemoryIdx(0))

    align_ceil = instruction.memory_bit_size.value // 8
    if 2**instruction.memarg.align > align_ceil:
        raise ValidationError("Invalid memarg alignment")

    ctx.pop_operand_and_assert_type(instruction.valtype)
    ctx.pop_operand_and_assert_type(ValType.i32)
Exemplo n.º 26
0
def get_export_type(context: Context, descriptor: TExportDesc) -> TExtern:
    """
    Helper function to validate the descriptor for an Export and return the
    associated type.
    """
    if isinstance(descriptor, FunctionIdx):
        return context.get_function(descriptor)
    elif isinstance(descriptor, TableIdx):
        return context.get_table(descriptor)
    elif isinstance(descriptor, MemoryIdx):
        return context.get_mem(descriptor)
    elif isinstance(descriptor, GlobalIdx):
        return context.get_global(descriptor)
    else:
        raise ValidationError(f"Unknown export descriptor type: {type(descriptor)}")
Exemplo n.º 27
0
    def pop_operand(self) -> Operand:
        """
        Pop a single operand off of the stack.  This can potentially return the
        `Unknown` sentinal value if the stack is polymorphic.
        """
        frame = self.control_stack.peek()

        if frame.is_unreachable and len(self.operand_stack) == frame.height:
            return Unknown
        elif len(self.operand_stack) <= frame.height:
            raise ValidationError(
                f"Underflow: Insufficient operands: {len(self.operand_stack)} <= "
                f"{frame.height}"
            )
        else:
            return self.operand_stack.pop()
Exemplo n.º 28
0
def validate_global(context: Context, global_: Global) -> None:
    expected_result_type = (global_.type.valtype, )

    with context.expression_context() as ctx:
        expression = Block.wrap((global_.type.valtype, ), global_.init)
        validate_expression(expression, ctx)

    result_type = ctx.result_type

    if result_type != expected_result_type:
        raise ValidationError(
            f"Initialization code fro global variable result type does not "
            f"match global value type: {result_type} != {expected_result_type}"
        )

    with context.expression_context() as ctx:
        validate_constant_expression(global_.init, ctx)
Exemplo n.º 29
0
def validate_data_segment(context: Context, data_segment: DataSegment) -> None:
    context.validate_mem_idx(data_segment.memory_idx)

    expected_result_type = (ValType.i32, )
    with context.expression_context() as ctx:
        expression = Block.wrap(expected_result_type, data_segment.offset)
        validate_expression(expression, ctx)

    result_type = ctx.result_type

    if result_type != expected_result_type:
        raise ValidationError(
            f"Invalid data segment.  Return type must be '(i32,)'.  Got "
            f"{result_type}")

    with context.expression_context() as ctx:
        validate_constant_expression(data_segment.offset, ctx)
Exemplo n.º 30
0
def validate_export_descriptor(context: Context,
                               descriptor: TExportDesc) -> TExportValue:
    if isinstance(descriptor, FunctionIdx):
        context.validate_function_idx(descriptor)
        return context.get_function(descriptor)
    elif isinstance(descriptor, TableIdx):
        context.validate_table_idx(descriptor)
        return context.get_table(descriptor)
    elif isinstance(descriptor, MemoryIdx):
        context.validate_mem_idx(descriptor)
        return context.get_mem(descriptor)
    elif isinstance(descriptor, GlobalIdx):
        context.validate_global_idx(descriptor)
        return context.get_global(descriptor)
    else:
        raise ValidationError(
            f"Unknown export descriptor type: {type(descriptor)}")