[energy-scan-server] reject scan request with zero channel mask (#12137)

This commit validates that the Channel Mask TLVs in a TMF Energy
Scan request are non-zero.

Additionally, this commit clamps the Count TLV value to the valid
range (1, 2, and 3) as required by the Thread specification.
The `test_otci` is updated to use count 3 (previously 4).

An Energy Scan request with a zero `Channel Mask` is invalid and
can cause the device to start a scan that takes a long time or
never completes. This change rejects such requests, preventing the
device from getting stuck. This was discovered by
fuzzer test.
This commit is contained in:
Abtin Keshavarzian
2025-11-12 09:21:29 +01:00
committed by GitHub
parent 372fe4fbf1
commit beeef5f8a6
3 changed files with 7 additions and 2 deletions
+3
View File
@@ -62,10 +62,13 @@ void EnergyScanServer::HandleTmf<kUriEnergyScan>(Coap::Message &aMessage, const
VerifyOrExit(aMessage.IsPostRequest()); VerifyOrExit(aMessage.IsPostRequest());
SuccessOrExit(Tlv::Find<MeshCoP::CountTlv>(aMessage, count)); SuccessOrExit(Tlv::Find<MeshCoP::CountTlv>(aMessage, count));
count = Clamp(count, kMinCount, kMaxCount);
SuccessOrExit(Tlv::Find<MeshCoP::PeriodTlv>(aMessage, period)); SuccessOrExit(Tlv::Find<MeshCoP::PeriodTlv>(aMessage, period));
SuccessOrExit(Tlv::Find<MeshCoP::ScanDurationTlv>(aMessage, scanDuration)); SuccessOrExit(Tlv::Find<MeshCoP::ScanDurationTlv>(aMessage, scanDuration));
SuccessOrExit(MeshCoP::ChannelMaskTlv::FindIn(aMessage, mask)); SuccessOrExit(MeshCoP::ChannelMaskTlv::FindIn(aMessage, mask));
VerifyOrExit(mask != 0);
mReportMessage.Reset(Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriEnergyReport)); mReportMessage.Reset(Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriEnergyReport));
VerifyOrExit(mReportMessage != nullptr); VerifyOrExit(mReportMessage != nullptr);
+2
View File
@@ -66,6 +66,8 @@ public:
private: private:
static constexpr uint32_t kScanDelay = 1000; // SCAN_DELAY (milliseconds) static constexpr uint32_t kScanDelay = 1000; // SCAN_DELAY (milliseconds)
static constexpr uint32_t kReportDelay = 500; // Delay before sending a report (milliseconds) static constexpr uint32_t kReportDelay = 500; // Delay before sending a report (milliseconds)
static constexpr uint8_t kMinCount = 1;
static constexpr uint8_t kMaxCount = 3;
template <Uri kUri> void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); template <Uri kUri> void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
+2 -2
View File
@@ -754,10 +754,10 @@ class TestOTCI(unittest.TestCase):
rtt: Dict[str, float] = cast(Dict[str, float], statistics['round_trip_time']) rtt: Dict[str, float] = cast(Dict[str, float], statistics['round_trip_time'])
self.assertTrue(rtt['min'] - 1e-9 <= rtt['avg'] <= rtt['max'] + 1e-9) self.assertTrue(rtt['min'] - 1e-9 <= rtt['avg'] <= rtt['max'] + 1e-9)
ed_report = commissioner.commissioner_energy_scan(3 << commissioner.get_channel(), 4, 32, 1000, ed_report = commissioner.commissioner_energy_scan(3 << commissioner.get_channel(), 3, 32, 1000,
child1.get_ipaddr_rloc()) child1.get_ipaddr_rloc())
comm_chan = commissioner.get_channel() comm_chan = commissioner.get_channel()
self.assertEqual({comm_chan: [-30, -30, -30, -30], comm_chan + 1: [-30, -30, -30, -30]}, ed_report) self.assertEqual({comm_chan: [-30, -30, -30], comm_chan + 1: [-30, -30, -30]}, ed_report)
commissioner.commissioner_announce(TEST_CHANNEL_MASK, 1, 32, child1.get_ipaddr_rloc()) commissioner.commissioner_announce(TEST_CHANNEL_MASK, 1, 32, child1.get_ipaddr_rloc())