Skip to content

Commit 7de782f

Browse files
committed
Fix float overflow silent truncation to inf and stale serializer cache
- Add FLT_MAX range check in SerFloatType.serialize and SerVectorType._serialize_float to raise OverflowError for finite values outside float32 range, matching pure-Python struct.pack behavior - Move column_encryption_policy check before cache lookup in PreparedStatement._serializers so it is evaluated on every access
1 parent f88e051 commit 7de782f

2 files changed

Lines changed: 29 additions & 5 deletions

File tree

cassandra/query.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -544,16 +544,18 @@ def _serializers(self):
544544
545545
Returns a list of Serializer objects if Cython serializers are available
546546
and there is no column encryption policy, otherwise returns None.
547+
548+
The column_encryption_policy check is performed on every access (not
549+
cached) so that serializers are correctly bypassed if a policy is set
550+
after construction.
547551
"""
552+
if self.column_encryption_policy:
553+
return None
548554
try:
549555
return self.__serializers
550556
except AttributeError:
551557
pass
552-
if (
553-
_HAVE_CYTHON_SERIALIZERS
554-
and not self.column_encryption_policy
555-
and self.column_metadata
556-
):
558+
if _HAVE_CYTHON_SERIALIZERS and self.column_metadata:
557559
self.__serializers = _cython_make_serializers(
558560
[col.type for col in self.column_metadata]
559561
)

cassandra/serializers.pyx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ cqltype.serialize() classmethod.
2929
from libc.stdint cimport int32_t, uint32_t
3030
from libc.string cimport memcpy
3131
from libc.stdlib cimport malloc, free
32+
from libc.float cimport FLT_MAX
33+
from libc.math cimport isinf, isnan
3234
from cpython.bytes cimport PyBytes_FromStringAndSize
3335

3436
from cassandra import cqltypes
@@ -51,6 +53,24 @@ cdef class Serializer:
5153
raise NotImplementedError
5254

5355

56+
# ---------------------------------------------------------------------------
57+
# Float range check
58+
# ---------------------------------------------------------------------------
59+
60+
cdef inline void _check_float_range(double value) except *:
61+
"""Raise OverflowError for finite values outside float32 range.
62+
63+
This matches the behavior of struct.pack('>f', value), which raises
64+
OverflowError (via struct.error) for values that cannot be represented
65+
as a 32-bit IEEE 754 float. inf, -inf, and nan pass through unchanged.
66+
"""
67+
if not isinf(value) and not isnan(value):
68+
if value > <double>FLT_MAX or value < -<double>FLT_MAX:
69+
raise OverflowError(
70+
"Value %r too large for float32 (max %r)" % (value, FLT_MAX)
71+
)
72+
73+
5474
# ---------------------------------------------------------------------------
5575
# Scalar serializers
5676
# ---------------------------------------------------------------------------
@@ -59,6 +79,7 @@ cdef class SerFloatType(Serializer):
5979
"""Serialize a Python float to 4-byte big-endian IEEE 754."""
6080

6181
cpdef bytes serialize(self, object value, int protocol_version):
82+
_check_float_range(<double>value)
6283
cdef float val = <float>value
6384
cdef char out[4]
6485
cdef char *src = <char *>&val
@@ -196,6 +217,7 @@ cdef class SerVectorType(Serializer):
196217

197218
try:
198219
for i in range(self.vector_size):
220+
_check_float_range(<double>values[i])
199221
val = <float>values[i]
200222
src = <char *>&val
201223
dst = buf + i * 4

0 commit comments

Comments
 (0)