def deserialize_uleb128_as_u32(self) -> int: value = 0 for shift in range(0, 32, 7): byte = int.from_bytes(self.read(1), "little", signed=False) digit = byte & 0x7F value |= digit << shift if value > MAX_U32: raise st.DeserializationError( "Overflow while parsing uleb128-encoded uint32 value") if digit == byte: if shift > 0 and digit == 0: raise st.DeserializationError( "Invalid uleb128 number (unexpected zero digit)") return value raise st.DeserializationError( "Overflow while parsing uleb128-encoded uint32 value")
def deserialize_bool(self) -> bool: b = int.from_bytes(self.read(1), byteorder="little", signed=False) if b == 0: return False elif b == 1: return True else: raise st.DeserializationError("Unexpected boolean value:", b)
def check_that_key_slices_are_increasing(self, slice1: typing.Tuple[int, int], slice2: typing.Tuple[int, int]): key1 = bytes(self.input.getbuffer()[slice1[0]:slice1[1]]) key2 = bytes(self.input.getbuffer()[slice2[0]:slice2[1]]) if key1 >= key2: raise st.DeserializationError( "Serialized keys in a map must be ordered by increasing lexicographic order" )
def deserialize_any(self, obj_type) -> typing.Any: if obj_type in self.primitive_type_deserializer: return self.primitive_type_deserializer[obj_type]() elif hasattr(obj_type, "__origin__"): # Generic type types = getattr(obj_type, "__args__") if getattr(obj_type, "__origin__") == collections.abc.Sequence: # Sequence assert len(types) == 1 item_type = types[0] length = self.deserialize_len() result = [] for i in range(0, length): item = self.deserialize_any(item_type) result.append(item) return result elif getattr(obj_type, "__origin__") == tuple: # Tuple result = [] for i in range(len(types)): item = self.deserialize_any(types[i]) result.append(item) return tuple(result) elif getattr(obj_type, "__origin__") == typing.Union: # Option assert len(types) == 2 and types[1] == type(None) tag = int.from_bytes(self.read(1), byteorder="little", signed=False) if tag == 0: return None elif tag == 1: return self.deserialize_any(types[0]) else: raise st.DeserializationError("Wrong tag for Option value") elif getattr(obj_type, "__origin__") == dict: # Map assert len(types) == 2 length = self.deserialize_len() result = dict() previous_key_slice = None for i in range(0, length): key_start = self.get_buffer_offset() key = self.deserialize_any(types[0]) key_end = self.get_buffer_offset() value = self.deserialize_any(types[1]) key_slice = (key_start, key_end) if previous_key_slice is not None: self.check_that_key_slices_are_increasing( previous_key_slice, key_slice ) previous_key_slice = key_slice result[key] = value return result else: raise st.DeserializationError("Unexpected type", obj_type) else: # handle structs if dataclasses.is_dataclass(obj_type): values = [] fields = dataclasses.fields(obj_type) typing_hints = get_type_hints(obj_type) self.increase_container_depth() for field in fields: field_type = typing_hints[field.name] field_value = self.deserialize_any(field_type) values.append(field_value) self.decrease_container_depth() return obj_type(*values) # handle variant elif hasattr(obj_type, "VARIANTS"): variant_index = self.deserialize_variant_index() if variant_index not in range(len(obj_type.VARIANTS)): raise st.DeserializationError( "Unexpected variant index", variant_index ) new_type = obj_type.VARIANTS[variant_index] return self.deserialize_any(new_type) else: raise st.DeserializationError("Unexpected type", obj_type)
def increase_container_depth(self): if self.container_depth_budget is not None: if self.container_depth_budget == 0: raise st.DeserializationError("Exceeded maximum container depth") self.container_depth_budget -= 1
def deserialize_str(self) -> str: content = self.deserialize_bytes() try: return content.decode() except UnicodeDecodeError: raise st.DeserializationError("Invalid unicode string:", content)
def read(self, length: int) -> bytes: value = self.input.read(length) if value is None or len(value) < length: raise st.DeserializationError("Input is too short") return value
def deserialize_len(self) -> int: value = int.from_bytes(self.read(8), byteorder="little", signed=False) if value > MAX_LENGTH: raise st.DeserializationError("Length exceeds the maximum supported value.") return value
def deserialize_len(self) -> int: value = self.deserialize_uleb128_as_u32() if value > MAX_LENGTH: raise st.DeserializationError( "Length exceeds the maximum supported value.") return value