Commit c54273dc authored by Martin Larralde's avatar Martin Larralde
Browse files

Fix unsound decoding of `Matrix` and `Vector` from bytes without checking endianness

parent 7d4df801
Pipeline #34945 passed with stage
in 4 minutes and 26 seconds
......@@ -858,21 +858,30 @@ cdef class Vector:
return vec
@classmethod
def _from_raw_bytes(cls, buffer, int n):
"""_from_raw_bytes(cls, buffer, n)\n--
def _from_raw_bytes(cls, object buffer, int n, str byteorder):
f"""_from_raw_bytes(cls, buffer, n, byteorder)\n--
Create a new vector using the given bytes to fill its contents.
"""
cdef Vector vec = cls.zeros(n)
cdef size_t itemsize = vec.itemsize
cdef object view = memoryview(buffer)
cdef uint8_t[::1] bytes = view.cast("B")
cdef const uint8_t[::1] bytes
cdef Vector vec = cls.zeros(n)
cdef size_t itemsize = vec.itemsize
cdef object view = memoryview(buffer)
# fix endianness if needed
if byteorder != SYS_BYTEORDER and vec.itemsize > 1:
newbuffer = array.array(vec.format)
newbuffer.frombytes(view)
newbuffer.byteswap()
view = memoryview(newbuffer)
# assign the items
bytes = view.cast("B")
assert bytes.shape[0] == n * vec.itemsize
if n > 0:
with nogil:
memcpy(vec._data, &bytes[0], vec._n * itemsize)
memcpy(vec._data, &bytes[0], n * itemsize)
return vec
# --- Magic methods ------------------------------------------------------
......@@ -960,7 +969,7 @@ cdef class Vector:
buffer = array.array(self.format)
buffer.frombytes(memoryview(self).cast("b"))
return self._from_raw_bytes, (buffer, self._n)
return self._from_raw_bytes, (buffer, self._n, SYS_BYTEORDER)
def __add__(Vector self, object other):
assert self._data != NULL
......@@ -1996,23 +2005,30 @@ cdef class Matrix:
return mat
@classmethod
def _from_raw_bytes(cls, buffer, int m, int n):
"""_from_raw_bytes(cls, buffer, m, n)\n--
def _from_raw_bytes(cls, buffer, int m, int n, str byteorder):
"""_from_raw_bytes(cls, buffer, m, n, byteorder)\n--
Create a new matrix using the given bytes to fill its contents.
"""
cdef Matrix mat = cls.zeros(m, n)
cdef size_t itemsize = mat.itemsize
cdef object view = memoryview(buffer)
cdef uint8_t[::1] bytes = view.cast("B")
cdef float** data = <float**> mat._data
cdef const uint8_t[::1] bytes
cdef Matrix mat = cls.zeros(m, n)
cdef size_t itemsize = mat.itemsize
cdef object view = memoryview(buffer)
# fix endianness if needed
if byteorder != SYS_BYTEORDER and mat.itemsize > 1:
newbuffer = array.array(mat.format)
newbuffer.frombytes(view)
newbuffer.byteswap()
view = memoryview(newbuffer)
# assign the items
bytes = view.cast("B")
assert bytes.shape[0] == m * n * itemsize
with nogil:
memcpy(data[0], &bytes[0], m * n * itemsize)
if n > 0 and m > 0:
with nogil:
memcpy(mat._data[0], &bytes[0], m * n * itemsize)
return mat
# --- Magic methods ------------------------------------------------------
......@@ -2103,7 +2119,7 @@ cdef class Matrix:
buffer = array.array(self.format)
buffer.frombytes(memoryview(self).cast("b"))
return self._from_raw_bytes, (buffer, self._m, self._n)
return self._from_raw_bytes, (buffer, self._m, self._n, SYS_BYTEORDER)
def __add__(Matrix self, object other):
assert self._data != NULL
......
......@@ -6342,7 +6342,7 @@ cdef class TopHits:
unsrt.append(state)
for i in range(self._th.N):
offset = (<ptrdiff_t> self._th.hit[i] - <ptrdiff_t> &self._th.unsrt[0]) / sizeof(P7_HIT)
offset = (<ptrdiff_t> self._th.hit[i] - <ptrdiff_t> &self._th.unsrt[0]) // sizeof(P7_HIT)
hits.append(offset)
return {
......
......@@ -260,6 +260,16 @@ class TestMatrixF(_TestMatrixBase, unittest.TestCase):
with self.assertRaises(IndexError):
x = mat[10, 0]
def test_from_raw_bytes_littleendian(self):
mat = self.Matrix._from_raw_bytes(b'\x00\x00\x80>', 1, 1, byteorder="little")
self.assertEqual(len(mat), 1)
self.assertEqual(mat[0][0], 0.25)
def test_from_raw_bytes_bigendian(self):
mat = self.Matrix._from_raw_bytes(b'>\x80\x00\x00', 1, 1, byteorder="big")
self.assertEqual(len(mat), 1)
self.assertEqual(mat[0][0], 0.25)
class TestMatrixU8(_TestMatrixBase, unittest.TestCase):
......
......@@ -366,6 +366,16 @@ class TestVectorF(_TestVectorBase, unittest.TestCase):
vec /= self.Vector([])
self.assertEqual(vec, self.Vector([]))
def test_from_raw_bytes_littleendian(self):
vec = self.Vector._from_raw_bytes(b'\x00\x00\x80>', 1, byteorder="little")
self.assertEqual(len(vec), 1)
self.assertEqual(vec[0], 0.25)
def test_from_raw_bytes_bigendian(self):
vec = self.Vector._from_raw_bytes(b'>\x80\x00\x00', 1, byteorder="big")
self.assertEqual(len(vec), 1)
self.assertEqual(vec[0], 0.25)
class TestVectorU8(_TestVectorBase, unittest.TestCase):
......
......@@ -103,6 +103,7 @@ class build_ext(_build_ext):
"SYS_VERSION_INFO_MAJOR": sys.version_info.major,
"SYS_VERSION_INFO_MINOR": sys.version_info.minor,
"SYS_VERSION_INFO_MICRO": sys.version_info.micro,
"SYS_BYTEORDER": sys.byteorder,
}
}
if hmmer_impl is not None:
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment