fix(ble_mesh): added internal buffer to store dfu targets

The protocol may still receive DFU-related packets from the
network (such as forwarded or retransmitted ones) after DFU ends,
while the user might release the targets immediately after DFU completion.
However, since the list head is not empty, the protocol could access
already-freed memory when iterating through the targets.


(cherry picked from commit 8c8b43f564)

Co-authored-by: luoxu <luoxu@espressif.com>
This commit is contained in:
Luo Xu
2026-02-09 17:43:25 +08:00
parent 98f2ad9a2a
commit 23001a291e
4 changed files with 98 additions and 3 deletions

View File

@@ -73,7 +73,8 @@ esp_err_t esp_ble_mesh_dfu_cli_img_send(esp_ble_mesh_dfu_cli_t *cli,
arg.send_arg.xfer = xfer;
return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_dfu_client_args_t),
NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
btc_ble_mesh_dfu_client_arg_deep_copy,
btc_ble_mesh_dfu_client_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
}
uint8_t esp_ble_mesh_dfu_cli_progress(esp_ble_mesh_dfu_cli_t *cli)

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -81,6 +81,11 @@ void btc_ble_mesh_dfu_client_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p
}
break;
}
case BTC_BLE_MESH_ACT_DFU_CLIENT_IMG_SEND:
/* That will be freed when dfu completed or failed not on btc deep free */
dst->send_arg.inputs =(struct esp_ble_mesh_blob_cli_inputs *)
dfu_targets_alloc((struct bt_mesh_blob_cli_inputs *)src->send_arg.inputs);
break;
default:
BT_DBG("%s, Unknown act %d", __func__, msg->act);
break;

View File

@@ -1,6 +1,6 @@
/*
* SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA
* SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileContributor: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -64,6 +64,83 @@ static struct {
sys_slist_t list;
} dfu_req_list;
static struct bt_mesh_blob_cli_inputs cur_targets = {0};
/**
* inputs list must point to a list of bt_mesh_dfu_target nodes.
* That was required by dfu api
*/
void dfu_targets_free(void)
{
sys_snode_t *n, *sn;
struct bt_mesh_dfu_target *target;
struct bt_mesh_blob_cli_inputs *inputs = &cur_targets;
if (cur_targets.targets.head == NULL) {
return;
}
SYS_SLIST_FOR_EACH_NODE_SAFE(&inputs->targets, n, sn) {
target = (struct bt_mesh_dfu_target *)n;
if (target->blob.pull) {
bt_mesh_free(target->blob.pull);
}
bt_mesh_free(target);
}
inputs->app_idx = 0;
inputs->group = 0;
inputs->ttl = 0;
inputs->timeout_base = 0;
sys_slist_init(&inputs->targets);
}
struct bt_mesh_blob_cli_inputs *dfu_targets_alloc(struct bt_mesh_blob_cli_inputs *src)
{
sys_snode_t *node;
struct bt_mesh_dfu_target *target_src = NULL;
struct bt_mesh_dfu_target *target_dst = NULL;
struct bt_mesh_blob_cli_inputs *dst = &cur_targets;
if (cur_targets.targets.head != NULL ||
cur_targets.targets.tail != NULL) {
BT_WARN("DFU targets busy");
return NULL;
}
dst->app_idx = src->app_idx;
dst->group = src->group;
dst->ttl = src->ttl;
dst->timeout_base = src->timeout_base;
sys_slist_init(&dst->targets);
SYS_SLIST_FOR_EACH_NODE(&src->targets, node) {
target_src = (struct bt_mesh_dfu_target *)node;
target_dst = bt_mesh_calloc(sizeof(struct bt_mesh_dfu_target));
if (!target_dst) {
dfu_targets_free();
return NULL;
}
memcpy(target_dst, target_src, sizeof(struct bt_mesh_dfu_target));
if (target_dst->blob.pull) {
target_dst->blob.pull = bt_mesh_calloc(sizeof(struct bt_mesh_blob_target_pull));
if (!target_dst->blob.pull) {
dfu_targets_free();
return NULL;
}
memcpy(target_dst->blob.pull, target_src->blob.pull, sizeof(struct bt_mesh_blob_target_pull));
} else {
target_dst->blob.pull = NULL;
}
target_dst->blob.n.next = NULL;
sys_slist_append(&dst->targets, &target_dst->blob.n);
}
return dst;
}
static struct bt_mesh_dfu_target *target_get(struct bt_mesh_dfu_cli *cli,
uint16_t addr)
{
@@ -277,6 +354,8 @@ static void dfu_failed(struct bt_mesh_dfu_cli *cli,
if (cli->cb && cli->cb->ended) {
cli->cb->ended(cli, reason);
}
dfu_targets_free();
}
static int req_setup(struct bt_mesh_dfu_cli *cli, enum req type,
@@ -839,6 +918,7 @@ static void confirmed(struct bt_mesh_blob_cli *b)
if (cli->cb && cli->cb->confirmed) {
cli->cb->confirmed(cli);
}
dfu_targets_free();
} else {
dfu_failed(cli, BLE_MESH_DFU_ERR_INTERNAL);
}
@@ -1202,6 +1282,12 @@ static void dfu_cli_reset(struct bt_mesh_model *mod)
{
struct bt_mesh_dfu_cli *cli = mod->user_data;
if (cli->xfer.state == STATE_IDLE) {
return;
}
dfu_targets_free();
bt_mesh_dfu_req_list_free();
cli->req = NULL;
cli->xfer.state = STATE_IDLE;

View File

@@ -261,6 +261,9 @@ struct bt_mesh_dfu_cli_xfer {
const struct bt_mesh_dfu_cli_xfer_blob_params *blob_params;
};
void dfu_targets_free(void);
struct bt_mesh_blob_cli_inputs *dfu_targets_alloc(struct bt_mesh_blob_cli_inputs *src);
/** @brief Start distributing a DFU.
*
* Starts distribution of the firmware in the given slot to the list of DFU