From a5cfdef4ed94eb42223ac09507dfd04bf4a924cb Mon Sep 17 00:00:00 2001 From: Luo Xu Date: Mon, 6 Jul 2026 17:43:59 +0800 Subject: [PATCH] fix(ble_mesh): validate PB-ADV start segment length (cherry picked from commit 912ec8dc62c80bc6d410213fa3688bae71ab3e7f) Co-authored-by: luoxu --- components/bt/esp_ble_mesh/core/prov_common.c | 51 ++++++++++++++----- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/components/bt/esp_ble_mesh/core/prov_common.c b/components/bt/esp_ble_mesh/core/prov_common.c index 2e9985e9331..e72d82fa815 100644 --- a/components/bt/esp_ble_mesh/core/prov_common.c +++ b/components/bt/esp_ble_mesh/core/prov_common.c @@ -188,6 +188,8 @@ bool bt_mesh_gen_prov_start(struct bt_mesh_prov_link *link, struct net_buf_simple *buf, struct prov_rx *rx, bool *close) { + uint8_t last_seg = START_LAST_SEG(rx->gpc); + if (link->rx.seg) { BT_INFO("Get Start while there are unreceived segments"); return false; @@ -215,7 +217,7 @@ bool bt_mesh_gen_prov_start(struct bt_mesh_prov_link *link, link->rx.fcs = net_buf_simple_pull_u8(buf); BT_DBG("LinkId:%08x,len %u last_seg %u total_len %u fcs 0x%02x", link->link_id, buf->len, - START_LAST_SEG(rx->gpc), link->rx.buf->len, link->rx.fcs); + last_seg, link->rx.buf->len, link->rx.fcs); /* At least one-octet pdu type is needed */ if (link->rx.buf->len < 1) { @@ -226,8 +228,8 @@ bool bt_mesh_gen_prov_start(struct bt_mesh_prov_link *link, return false; } - if (START_LAST_SEG(rx->gpc) > START_LAST_SEG_MAX) { - BT_ERR("Invalid SegN 0x%02x", START_LAST_SEG(rx->gpc)); + if (last_seg > START_LAST_SEG_MAX) { + BT_ERR("Invalid SegN 0x%02x", last_seg); if (close) { *close = true; } @@ -243,16 +245,19 @@ bool bt_mesh_gen_prov_start(struct bt_mesh_prov_link *link, return false; } - if (START_LAST_SEG(rx->gpc) > 0) { - /* For multi-segment PDUs, validate that total length is consistent - * with the claimed segment count to prevent underflow in - * bt_mesh_gen_prov_cont() when computing expect_len. - * Minimum length = first segment (20) + (last_seg - 1) * full continuation (23) + 1 + /* Validate that the declared total length and the actual start-segment + * payload length match the claimed segment count before copying segment 0. + * This keeps malformed extended advertising reports from writing past the + * fixed provisioning rx buffer. + */ + if (last_seg > 0) { + /* Minimum length = first segment (20) + (last_seg - 1) * + * full continuation (23) + one byte in the last segment. */ - uint16_t min_len = 20 + 23 * (START_LAST_SEG(rx->gpc) - 1) + 1; + uint16_t min_len = START_PAYLOAD_MAX + CONT_PAYLOAD_MAX * (last_seg - 1) + 1; if (link->rx.buf->len < min_len) { BT_ERR("Total length %u too small for %u segments (min %u)", - link->rx.buf->len, START_LAST_SEG(rx->gpc) + 1, min_len); + link->rx.buf->len, last_seg + 1, min_len); if (close) { *close = true; } @@ -260,8 +265,30 @@ bool bt_mesh_gen_prov_start(struct bt_mesh_prov_link *link, } } - link->rx.seg = (1 << (START_LAST_SEG(rx->gpc) + 1)) - 1; - link->rx.last_seg = START_LAST_SEG(rx->gpc); + { + uint16_t max_len = START_PAYLOAD_MAX + CONT_PAYLOAD_MAX * last_seg; + uint16_t seg_0_len = last_seg ? START_PAYLOAD_MAX : link->rx.buf->len; + + if (link->rx.buf->len > max_len) { + BT_ERR("Total length %u too large for %u segments (max %u)", + link->rx.buf->len, last_seg + 1, max_len); + if (close) { + *close = true; + } + return false; + } + + if (buf->len != seg_0_len) { + BT_ERR("Invalid start segment len %u != %u", buf->len, seg_0_len); + if (close) { + *close = true; + } + return false; + } + } + + link->rx.seg = (1 << (last_seg + 1)) - 1; + link->rx.last_seg = last_seg; memcpy(link->rx.buf->data, buf->data, buf->len); XACT_SEG_RECV(link, 0); BT_DBG("Seg: %04x, lastSeg: %04x, Data: %s", link->rx.seg, link->rx.last_seg, bt_hex(buf->data, buf->len));