11#include " object_spy_message.h"
22
3+ size_t spy_message_extra_data_length (const icsSpyMessage& msg)
4+ {
5+ if (msg.Protocol == SPY_PROTOCOL_A2B || msg.Protocol == SPY_PROTOCOL_ETHERNET ||
6+ msg.Protocol == SPY_PROTOCOL_SPI || msg.Protocol == SPY_PROTOCOL_WBMS )
7+ return (msg.NumberBytesHeader << 8 ) | msg.NumberBytesData ;
8+ return msg.NumberBytesData ;
9+ }
10+
11+ bool spy_message_validate_extra_data (const spy_message_object* obj)
12+ {
13+ if (obj->msg .ExtraDataPtr && spy_message_extra_data_length (obj->msg ) > obj->extraDataCapacity ) {
14+ PyErr_SetString (PyExc_ValueError, " ExtraDataPtr length exceeds its buffer capacity" );
15+ return false ;
16+ }
17+ return true ;
18+ }
19+
20+ void spy_message_record_received_extra_data (PyObject* o)
21+ {
22+ spy_message_object* obj = (spy_message_object*)o;
23+ // The DLL owns the pointer; its original reported length is the readable bound.
24+ obj->extraDataCapacity = obj->msg .ExtraDataPtr ? spy_message_extra_data_length (obj->msg ) : 0 ;
25+ obj->noExtraDataPtrCleanup = true ;
26+ }
27+
328static int spy_message_object_alloc (spy_message_object* self, PyObject* args, PyObject* kwds)
429{
530 (void )args;
631 (void )kwds;
732 memset (&self->msg , 0 , sizeof (self->msg ));
833 self->noExtraDataPtrCleanup = false ;
34+ self->extraDataCapacity = 0 ;
935 return 0 ;
1036}
1137
@@ -98,6 +124,8 @@ static PyObject* spy_message_object_getattr(PyObject* o, PyObject* attr_name)
98124 Py_DECREF (temp);
99125 return data;
100126 } else if (PyUnicode_CompareWithASCIIString (attr_name, " ExtraDataPtr" ) == 0 ) {
127+ if (!spy_message_validate_extra_data ((spy_message_object*)o))
128+ return NULL ;
101129 spy_message_j1850_object* obj = (spy_message_j1850_object*)o;
102130 unsigned char * ExtraDataPtr = (unsigned char *)obj->msg .ExtraDataPtr ;
103131 bool extra_data_ptr_enabled = obj->msg .ExtraDataPtrEnabled != 0 ;
@@ -108,14 +136,7 @@ static PyObject* spy_message_object_getattr(PyObject* o, PyObject* attr_name)
108136 && obj->msg .ExtraDataPtr != NULL ) {
109137 extra_data_ptr_enabled = true ;
110138 }
111- int actual_size = 0 ;
112- // Some newer protocols are packing the length into NumberBytesHeader also so lets handle it here...
113- if (obj->msg .Protocol == SPY_PROTOCOL_A2B || obj->msg .Protocol == SPY_PROTOCOL_ETHERNET ||
114- obj->msg .Protocol == SPY_PROTOCOL_SPI || obj->msg .Protocol == SPY_PROTOCOL_WBMS ) {
115- actual_size = (obj->msg .NumberBytesHeader << 8 ) | obj->msg .NumberBytesData ;
116- } else {
117- actual_size = obj->msg .NumberBytesData ;
118- }
139+ size_t actual_size = spy_message_extra_data_length (((spy_message_object*)o)->msg );
119140 if (extra_data_ptr_enabled && actual_size && obj->msg .ExtraDataPtr ) {
120141 PyObject* tuple = PyTuple_New (actual_size);
121142 for (int i = 0 ; i < actual_size; ++i) {
@@ -130,7 +151,7 @@ static PyObject* spy_message_object_getattr(PyObject* o, PyObject* attr_name)
130151 }
131152}
132153
133- static int spy_message_object_setattr (PyObject* o, PyObject* name, PyObject* value)
154+ static int spy_message_object_setattr_impl (PyObject* o, PyObject* name, PyObject* value)
134155{
135156 // tp_setattro receives NULL for deletion. Let descriptors reject deletion
136157 // before any custom setter inspects the value or changes message storage.
@@ -155,14 +176,6 @@ static int spy_message_object_setattr(PyObject* o, PyObject* name, PyObject* val
155176 return -1 ;
156177 obj->msg .NumberBytesHeader = static_cast <uint8_t >(length);
157178 return 0 ;
158- } else if (PyUnicode_CompareWithASCIIString (name, " Protocol" ) == 0 ) {
159- // Ethernet behavior is backward to CAN and will crash if enabled.
160- long protocol = PyLong_AsLong (value);
161- if (protocol == -1 && PyErr_Occurred ())
162- PyErr_Clear (); // let PyObject_GenericSetAttr report the type error
163- else if (protocol == SPY_PROTOCOL_ETHERNET )
164- obj->msg .ExtraDataPtrEnabled = 0 ;
165- return PyObject_GenericSetAttr (o, name, value);
166179 } else if (PyUnicode_CompareWithASCIIString (name, " ExtraDataPtr" ) == 0 ) {
167180 if (!PyTuple_Check (value)) {
168181 PyErr_Format (PyExc_AttributeError,
@@ -200,6 +213,7 @@ static int spy_message_object_setattr(PyObject* o, PyObject* name, PyObject* val
200213 if (obj->msg .ExtraDataPtr != NULL && !obj->noExtraDataPtrCleanup )
201214 delete[] (unsigned char *)obj->msg .ExtraDataPtr ;
202215 obj->msg .ExtraDataPtr = buffer;
216+ obj->extraDataCapacity = (size_t )length;
203217 if (packs_length)
204218 obj->msg .NumberBytesHeader = static_cast <uint8_t >(length >> 8 );
205219 obj->msg .NumberBytesData = static_cast <uint8_t >(length & 0xFF );
@@ -219,6 +233,7 @@ static int spy_message_object_setattr(PyObject* o, PyObject* name, PyObject* val
219233 if (obj->msg .ExtraDataPtr != NULL ) {
220234 delete[] (unsigned char *)obj->msg .ExtraDataPtr ;
221235 obj->msg .ExtraDataPtr = NULL ;
236+ obj->extraDataCapacity = 0 ;
222237 }
223238 } else if (enabled != 0 && obj->msg .Protocol == SPY_PROTOCOL_ETHERNET ) {
224239 // Ethernet always needs to be set to 0
@@ -230,6 +245,81 @@ static int spy_message_object_setattr(PyObject* o, PyObject* name, PyObject* val
230245 }
231246}
232247
248+ static int spy_message_object_setattr (PyObject* o, PyObject* name, PyObject* value)
249+ {
250+ // Subclass deletion descriptors may execute Python. Never include them in
251+ // a native-message rollback transaction.
252+ if (!value)
253+ return PyObject_GenericSetAttr (o, name, value);
254+ // These assignments can reinterpret or enlarge the payload length. Roll back
255+ // the complete native message, including Data/Header bytes, on failure.
256+ bool scalar_length = PyUnicode_CompareWithASCIIString (name, " NumberBytesData" ) == 0 ||
257+ PyUnicode_CompareWithASCIIString (name, " NumberBytesHeader" ) == 0 ||
258+ PyUnicode_CompareWithASCIIString (name, " Protocol" ) == 0 ;
259+ bool data = PyUnicode_CompareWithASCIIString (name, " Data" ) == 0 ;
260+ bool header = PyUnicode_CompareWithASCIIString (name, " Header" ) == 0 ;
261+ bool changes_length = scalar_length || data || header;
262+ // __index__ can execute arbitrary Python, including replacing the payload.
263+ // Finish all such conversions BEFORE snapshotting the message for rollback.
264+ PyObject* normalized = NULL ;
265+ if (value && scalar_length) {
266+ normalized = PyNumber_Index (value);
267+ if (!normalized)
268+ return -1 ;
269+ long byte = PyLong_AsLong (normalized);
270+ if ((byte == -1 && PyErr_Occurred ()) || byte < 0 || byte > 255 ) {
271+ Py_DECREF (normalized);
272+ if (!PyErr_Occurred ())
273+ PyErr_SetString (PyExc_ValueError, " Message length and protocol fields must be in range 0..255" );
274+ return -1 ;
275+ }
276+ // Reject truncation rather than letting T_UBYTE issue a warning inside
277+ // the transaction: a user warning hook can also replace the payload.
278+ } else if (value && (data || header) && PyTuple_Check (value) &&
279+ PyTuple_Size (value) <= (data ? 8 : 4 )) {
280+ normalized = PyTuple_New (PyTuple_Size (value));
281+ if (!normalized)
282+ return -1 ;
283+ for (Py_ssize_t i = 0 ; i < PyTuple_Size (value); ++i) {
284+ PyObject* byte = PyNumber_Index (PyTuple_GetItem (value, i));
285+ if (!byte) {
286+ Py_DECREF (normalized);
287+ return -1 ;
288+ }
289+ PyTuple_SET_ITEM (normalized, i, byte);
290+ }
291+ }
292+ spy_message_object* obj = (spy_message_object*)o;
293+ icsSpyMessage original = obj->msg ;
294+ int result = 0 ;
295+ if (scalar_length) {
296+ // These native fields, like Data/Header, must not dispatch subclass
297+ // descriptors inside the transaction: they can replace/free payloads.
298+ unsigned char byte = (unsigned char )PyLong_AsLong (normalized);
299+ if (PyUnicode_CompareWithASCIIString (name, " NumberBytesData" ) == 0 )
300+ obj->msg .NumberBytesData = byte;
301+ else if (PyUnicode_CompareWithASCIIString (name, " NumberBytesHeader" ) == 0 )
302+ obj->msg .NumberBytesHeader = byte;
303+ else {
304+ obj->msg .Protocol = byte;
305+ if (byte == SPY_PROTOCOL_ETHERNET )
306+ obj->msg .ExtraDataPtrEnabled = 0 ;
307+ }
308+ } else {
309+ result = spy_message_object_setattr_impl (o, name, normalized ? normalized : value);
310+ }
311+ Py_XDECREF (normalized);
312+ if (result == 0 && changes_length && obj->msg .ExtraDataPtr &&
313+ spy_message_extra_data_length (obj->msg ) > obj->extraDataCapacity ) {
314+ // Restore before allocating the exception: cyclic-GC finalizers may
315+ // execute Python during allocation and must see the committed state.
316+ obj->msg = original;
317+ PyErr_SetString (PyExc_ValueError, " ExtraDataPtr length exceeds its buffer capacity" );
318+ return -1 ;
319+ }
320+ return result;
321+ }
322+
233323static PyMemberDef spy_message_object_members[] = {
234324 { " StatusBitField" , T_UINT , offsetof (spy_message_object, msg.StatusBitField ), 0 , " StatusBitField" },
235325 { " StatusBitField2" , T_UINT , offsetof (spy_message_object, msg.StatusBitField2 ), 0 , " StatusBitField2" },
0 commit comments