Skip to content

Commit 6972103

Browse files
authored
Handle bad values in XNCP chip info parsing (#688)
* Fail gracefully when parsing invalid XNCP chip info response * Add a test
1 parent 2ec05d0 commit 6972103

File tree

3 files changed

+44
-4
lines changed

3 files changed

+44
-4
lines changed

bellows/exception.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ class InvalidCommandError(EzspError):
99
pass
1010

1111

12+
class InvalidCommandPayload(InvalidCommandError):
13+
def __init__(self, msg: str, raw_bytes: bytes) -> None:
14+
super().__init__(msg)
15+
self.raw_bytes = raw_bytes
16+
17+
1218
class ControllerError(ControllerException):
1319
pass
1420

bellows/ezsp/__init__.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import zigpy.config
2323

2424
import bellows.config as conf
25-
from bellows.exception import EzspError, InvalidCommandError
25+
from bellows.exception import EzspError, InvalidCommandError, InvalidCommandPayload
2626
from bellows.ezsp import xncp
2727
from bellows.ezsp.config import DEFAULT_CONFIG, RuntimeConfig, ValueConfig
2828
from bellows.ezsp.xncp import FirmwareFeatures, FlowControlType
@@ -736,7 +736,9 @@ async def send_xncp_frame(
736736
try:
737737
rsp_frame = xncp.XncpCommand.from_bytes(data)
738738
except ValueError:
739-
raise InvalidCommandError(f"Invalid XNCP response: {data!r}")
739+
raise InvalidCommandPayload(
740+
f"Invalid XNCP response: {data!r}", raw_bytes=data
741+
)
740742

741743
if isinstance(rsp_frame.payload, xncp.Unknown):
742744
raise InvalidCommandError(f"XNCP firmware does not support {payload}")
@@ -781,7 +783,15 @@ async def xncp_get_flow_control_type(self) -> FlowControlType:
781783

782784
async def xncp_get_chip_info(self) -> xncp.GetChipInfoRsp:
783785
"""Get the part number."""
784-
return await self.send_xncp_frame(xncp.GetChipInfoReq())
786+
787+
try:
788+
return await self.send_xncp_frame(xncp.GetChipInfoReq())
789+
except InvalidCommandPayload:
790+
# Beta firmwares had a bug with this command
791+
return xncp.GetChipInfoRsp(
792+
ram_size=262144,
793+
part_number="EFR32MG24A420F1536IM40",
794+
)
785795

786796
async def get_default_adapter_concurrency(self) -> int:
787797
"""Get the recommended concurrency based on chip information."""

tests/test_ezsp.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from bellows import config, uart
1212
from bellows.ash import NcpFailure
13-
from bellows.exception import EzspError, InvalidCommandError
13+
from bellows.exception import EzspError, InvalidCommandError, InvalidCommandPayload
1414
from bellows.ezsp import EZSP, EZSP_LATEST, xncp
1515
import bellows.types as t
1616

@@ -992,6 +992,30 @@ async def test_xncp_get_chip_info(ezsp_f):
992992
assert mock_send.mock_calls == [call(xncp.GetChipInfoReq())]
993993

994994

995+
async def test_xncp_get_chip_info_invalid_payload_fallback(ezsp_f):
996+
"""Test graceful fallback when chip info command returns invalid payload."""
997+
ezsp_f._xncp_features = xncp.FirmwareFeatures.CHIP_INFO
998+
999+
with patch.object(
1000+
ezsp_f,
1001+
"send_xncp_frame",
1002+
new=AsyncMock(
1003+
side_effect=InvalidCommandPayload(
1004+
"Invalid XNCP response: b'\\x05\\x80\\x02'", b"\x05\x80\x02"
1005+
)
1006+
),
1007+
) as mock_send:
1008+
result = await ezsp_f.xncp_get_chip_info()
1009+
1010+
# Should return fallback response for beta firmware bug
1011+
assert result == xncp.GetChipInfoRsp(
1012+
ram_size=262144,
1013+
part_number="EFR32MG24A420F1536IM40",
1014+
)
1015+
1016+
assert mock_send.mock_calls == [call(xncp.GetChipInfoReq())]
1017+
1018+
9951019
@pytest.mark.parametrize(
9961020
"chip_info_available,ram_size,part_number,expected_concurrency",
9971021
[

0 commit comments

Comments
 (0)