Skip to content

Commit 1614ef2

Browse files
authored
fix: decode isotp infinite loop (#18)
1 parent 0adde6e commit 1614ef2

3 files changed

Lines changed: 35 additions & 47 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# Changelog
22
All notable changes to this project will be documented in this file.
33

4+
## [3.2.1]
5+
6+
### Features:
7+
- ``CanTp``: fix a case in ``decode_isotp`` that could lead to an infinite loop
8+
49
## [3.2.0]
510

611
### Features:

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
tests_require=["pytest", "pytest-mock"],
2929
extras_require={"test": ["pytest", "pytest-mock"]},
3030
# *strongly* suggested for sharing
31-
version="3.2.0",
31+
version="3.2.1",
3232
# The license can be anything you like
3333
license="MIT",
3434
description="Please use python-uds instead, this is a refactored version with breaking changes, only for pykiso",

uds/uds_communications/TransportProtocols/Can/CanTp.py

Lines changed: 29 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def __init__(self, connector=None, is_fd: bool = True, **kwargs):
8787
self._recv_buffer = queue.Queue()
8888
self._discard_negative_responses = Config.isotp.discard_neg_resp
8989

90-
# default STmin for flow control when receiving consecutive frames
90+
# default STmin for flow control when receiving consecutive frames
9191
self.st_min = 0.030
9292

9393
# sets up the relevant parameters in the instance
@@ -98,7 +98,7 @@ def __init__(self, connector=None, is_fd: bool = True, **kwargs):
9898

9999
self._max_frame_length = 64 if is_fd else 8
100100
# 7 bytes PDU for normal addressing, 6 for extended and mixed
101-
self._max_pdu_length = (self._max_frame_length - self._pdu_start_index - MINIMUM_HEADER_SIZE)
101+
self._max_pdu_length = self._max_frame_length - self._pdu_start_index - MINIMUM_HEADER_SIZE
102102
# maximum payload length of a 'classic' single frame with a message data length of 7 bytes at most (defined by ISO)
103103
self._single_frame_max_length_for_short_header = 0b111 - self._pdu_start_index
104104

@@ -109,7 +109,7 @@ def is_fd(self) -> bool:
109109
@is_fd.setter
110110
def is_fd(self, value: bool):
111111
self._max_frame_length = 64 if value is True else 8
112-
self._max_pdu_length = (self._max_frame_length - self._pdu_start_index - MINIMUM_HEADER_SIZE)
112+
self._max_pdu_length = self._max_frame_length - self._pdu_start_index - MINIMUM_HEADER_SIZE
113113

114114
@property
115115
def reqIdAddress(self):
@@ -142,53 +142,44 @@ def connection(self, value):
142142
def send(self, payload, functionalReq=False, tpWaitTime=0.01) -> None:
143143
result = self.encode_isotp(payload, functionalReq)
144144
return result
145-
145+
146146
def make_single_frame(self, payload: List[int]) -> List[int]:
147-
single_frame = [self.PADDING_PATTERN] * 8
147+
single_frame = [self.PADDING_PATTERN] * 8
148148
if not self.is_fd or len(payload) <= self._single_frame_max_length_for_short_header:
149149
# if we are not using CAN FD or the payload can be packed within 8 bytes, create a short frame
150150
# the MDL is then indicated on the low nibble of the 1st byte
151151
single_frame = [
152152
(CanTpMessageType.SINGLE_FRAME << 4) + len(payload),
153153
# pad the frame to send to have a total length of 8 bytes
154154
*fillArray(
155-
payload,
156-
length=self._single_frame_max_length_for_short_header,
157-
fillValue=self.PADDING_PATTERN
158-
)
155+
payload, length=self._single_frame_max_length_for_short_header, fillValue=self.PADDING_PATTERN
156+
),
159157
]
160158
else:
161159
# otherwise the MDL is indicated in the entire 2nd byte
162160
single_frame = [CanTpMessageType.SINGLE_FRAME, len(payload), *payload]
163161
single_frame = self.add_padding(single_frame)
164162
return single_frame
165-
163+
166164
def make_first_frame(self, payload: List[int]) -> List[int]:
167165
mdl = len(payload)
168166
mdl_high_nibble = (mdl & 0xF00) >> 8
169167
mdl_low_nibble = mdl & 0x0FF
170168
first_frame = [
171169
(CanTpMessageType.FIRST_FRAME << 4) + mdl_high_nibble,
172170
mdl_low_nibble,
173-
*payload[: self._max_pdu_length - 1]
171+
*payload[: self._max_pdu_length - 1],
174172
]
175173
first_frame = self.add_padding(first_frame)
176174
return first_frame
177-
175+
178176
def make_consecutive_frame(self, payload: List[int], sequence_number: int = 1) -> List[int]:
179-
consecutive_frame = [
180-
(CanTpMessageType.CONSECUTIVE_FRAME << 4) + sequence_number,
181-
*payload
182-
]
177+
consecutive_frame = [(CanTpMessageType.CONSECUTIVE_FRAME << 4) + sequence_number, *payload]
183178
consecutive_frame = self.add_padding(consecutive_frame)
184179
return consecutive_frame
185-
180+
186181
def make_flow_control_frame(self, blocksize: int = 0, st_min: float = 0) -> List[int]:
187-
flow_control_frame = [
188-
(CanTpMessageType.FLOW_CONTROL << 4),
189-
blocksize,
190-
self.encode_stMin(st_min)
191-
]
182+
flow_control_frame = [(CanTpMessageType.FLOW_CONTROL << 4), blocksize, self.encode_stMin(st_min)]
192183
flow_control_frame = self.add_padding(flow_control_frame)
193184
return flow_control_frame
194185

@@ -244,9 +235,7 @@ def encode_isotp(
244235
fs = rxPdu[0] & 0x0F
245236
if fs == CanTpFsTypes.CONTINUE_TO_SEND:
246237
if state != CanTpState.WAIT_FLOW_CONTROL:
247-
raise ValueError(
248-
"Received unexpected Flow Control Continue to Send request"
249-
)
238+
raise ValueError("Received unexpected Flow Control Continue to Send request")
250239

251240
block_size = rxPdu[FC_BS_INDEX]
252241
if block_size == 0:
@@ -265,7 +254,9 @@ def encode_isotp(
265254
else:
266255
raise ValueError(f"Unexpected fs response from ECU. {rxPdu}")
267256
else:
268-
logger.warning(f"Unexpected response from ECU while waiting for flow control: 0x{bytes(rxPdu).hex()}")
257+
logger.warning(
258+
f"Unexpected response from ECU while waiting for flow control: 0x{bytes(rxPdu).hex()}"
259+
)
269260

270261
if state == CanTpState.SEND_SINGLE_FRAME:
271262
txPdu = self.make_single_frame(payload)
@@ -344,28 +335,24 @@ def decode_isotp(
344335
if state == CanTpState.IDLE:
345336
if N_PCI == CanTpMessageType.SINGLE_FRAME:
346337
payloadLength = rxPdu[N_PCI_INDEX & 0x0F]
347-
payload = rxPdu[
348-
SINGLE_FRAME_DATA_START_INDEX : SINGLE_FRAME_DATA_START_INDEX
349-
+ payloadLength
350-
]
338+
payload = rxPdu[SINGLE_FRAME_DATA_START_INDEX : SINGLE_FRAME_DATA_START_INDEX + payloadLength]
351339
endOfMessage_flag = True
352340
elif N_PCI == CanTpMessageType.FIRST_FRAME:
353341
payload = rxPdu[FIRST_FRAME_DATA_START_INDEX:]
354-
payloadLength = (
355-
(rxPdu[FIRST_FRAME_DL_INDEX_HIGH] & 0x0F) << 8
356-
) + rxPdu[FIRST_FRAME_DL_INDEX_LOW]
342+
payloadLength = ((rxPdu[FIRST_FRAME_DL_INDEX_HIGH] & 0x0F) << 8) + rxPdu[FIRST_FRAME_DL_INDEX_LOW]
357343
payloadPtr = self._max_pdu_length - 1
358344
state = CanTpState.SEND_FLOW_CONTROL
345+
elif N_PCI == CanTpMessageType.CONSECUTIVE_FRAME:
346+
# Consecutive frames are not expected in idle state else we are in an infinite loop
347+
raise RuntimeError("Consecutive frames are not supported in idle state")
359348
elif state == CanTpState.RECEIVING_CONSECUTIVE_FRAME:
360349
if N_PCI == CanTpMessageType.CONSECUTIVE_FRAME:
361-
sequenceNumber = (
362-
rxPdu[CONSECUTIVE_FRAME_SEQUENCE_NUMBER_INDEX] & 0x0F
363-
)
350+
sequenceNumber = rxPdu[CONSECUTIVE_FRAME_SEQUENCE_NUMBER_INDEX] & 0x0F
364351
if sequenceNumber != sequenceNumberExpected:
365352
raise ValueError(
366353
f"Consecutive frame sequence out of order, expected {sequenceNumberExpected} got {sequenceNumber}"
367354
)
368-
355+
369356
sequenceNumberExpected = (sequenceNumberExpected + 1) % 16
370357
payload += rxPdu[CONSECUTIVE_FRAME_SEQUENCE_DATA_START_INDEX:]
371358
payloadPtr += self._max_pdu_length
@@ -425,7 +412,7 @@ def decode_stMin(val: int) -> float:
425412
raise ValueError(
426413
f"Invalid STMin value {hex(val)}, should be between 0x00 and 0x7F or between 0xF1 and 0xF9"
427414
)
428-
415+
429416
@staticmethod
430417
def encode_stMin(val: float) -> int:
431418
if (0x01 * 1e-3) <= val <= (0x7F * 1e-3):
@@ -435,9 +422,7 @@ def encode_stMin(val: float) -> int:
435422
# 100us - 900us -> 0xF1 - 0xF9
436423
return 0xF0 + int(val * 1e4)
437424
else:
438-
raise ValueError(
439-
f"Invalid STMin time {val}, should be between 0.1 and 0.9 ms or between 1 and 127 ms"
440-
)
425+
raise ValueError(f"Invalid STMin time {val}, should be between 0.1 and 0.9 ms or between 1 and 127 ms")
441426

442427
##
443428
# @brief creates the blocklist from the blocksize and payload
@@ -474,9 +459,9 @@ def create_blockList(self, payload: List[int], blockSize: int) -> List[List[int]
474459
blockPtr = 0
475460

476461
return blockList
477-
462+
478463
def add_padding(self, payload: List[int]) -> List[int]:
479-
"""Add padding to the payload to be sent over CAN.
464+
"""Add padding to the payload to be sent over CAN.
480465
481466
:param payload: payload to be sent.
482467
:param header_size: size of the ISO TP header of the CAN frame's payload.
@@ -498,9 +483,7 @@ def add_padding(self, payload: List[int]) -> List[int]:
498483

499484
##
500485
# @brief transmits the data over can using can connection
501-
def transmit(
502-
self, data, functionalReq=False, use_external_snd_rcv_functions: bool = False
503-
):
486+
def transmit(self, data, functionalReq=False, use_external_snd_rcv_functions: bool = False):
504487
# check functional request
505488
if functionalReq:
506489
raise Exception("Functional requests are currently not supported")

0 commit comments

Comments
 (0)