From 31dcb28c62eee9b106e09206307e1082061a2458 Mon Sep 17 00:00:00 2001 From: Claude Worker claude2 Date: Thu, 11 Jun 2026 05:18:55 +0200 Subject: [PATCH] fix(espnow): compteur peers reel + alias UAF scenario_mesh_peer_count() cable sur le stub sante (espnow_peers restait a 0) ; reponse POST /game/peers construite avant cJSON_Delete (use-after-free observe sur carte : alias illisible). Teste sur FNK0102H : register live + NVS recharge au boot (espnow_peers=2), relay -> timeout/unknown_peer attendus. --- idf_zacus/components/game_endpoint/game_endpoint.c | 5 +++-- idf_zacus/main/main.c | 2 +- lib/scenario_mesh/include/scenario_mesh.h | 4 ++++ lib/scenario_mesh/scenario_mesh.c | 8 ++++++++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/idf_zacus/components/game_endpoint/game_endpoint.c b/idf_zacus/components/game_endpoint/game_endpoint.c index c9520f4..e23e64e 100644 --- a/idf_zacus/components/game_endpoint/game_endpoint.c +++ b/idf_zacus/components/game_endpoint/game_endpoint.c @@ -595,12 +595,13 @@ static esp_err_t handle_peers_post(httpd_req_t *req) { ESP_LOGI(TAG, "peers: \"%s\" -> %02X:%02X:%02X:%02X:%02X:%02X (%s)", alias, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], reg == ESP_OK ? "live" : "reboot needed"); - cJSON_Delete(root); - + // Build the response BEFORE deleting root: `alias` points into the cJSON + // tree (use-after-free observed on-device as a garbled alias echo). char resp[128]; snprintf(resp, sizeof(resp), "{\"ok\":true,\"alias\":\"%s\",\"live\":%s}", alias, reg == ESP_OK ? "true" : "false"); + cJSON_Delete(root); return send_json(req, "200 OK", resp); } diff --git a/idf_zacus/main/main.c b/idf_zacus/main/main.c index b9a5819..519446e 100644 --- a/idf_zacus/main/main.c +++ b/idf_zacus/main/main.c @@ -89,7 +89,7 @@ int puzzle_get_battery_pct(void) { } int puzzle_get_espnow_peer_count(void) { - return 0; + return scenario_mesh_peer_count(); } // Slice 6: wake-word callback. Runs on the voice_pipeline capture task, diff --git a/lib/scenario_mesh/include/scenario_mesh.h b/lib/scenario_mesh/include/scenario_mesh.h index 2db86d4..cef0ad7 100644 --- a/lib/scenario_mesh/include/scenario_mesh.h +++ b/lib/scenario_mesh/include/scenario_mesh.h @@ -73,6 +73,10 @@ esp_err_t scenario_mesh_register_peer(const char *alias, const uint8_t mac[6]); // ESP_ERR_NOT_FOUND otherwise. esp_err_t scenario_mesh_mac_for_alias(const char *alias, uint8_t mac_out[6]); +// Number of peers currently registered in the live table (broadcast peer +// excluded). Backs the espnow_peers health counter. +int scenario_mesh_peer_count(void); + // Chunk `data` (len bytes) into frames and send them all sequentially to // `dest_mac`, awaiting the per-frame ack. Returns ESP_OK only if every frame // was acked; ESP_ERR_TIMEOUT if any frame ack timed out, or the underlying diff --git a/lib/scenario_mesh/scenario_mesh.c b/lib/scenario_mesh/scenario_mesh.c index 89d10e3..e845fb9 100644 --- a/lib/scenario_mesh/scenario_mesh.c +++ b/lib/scenario_mesh/scenario_mesh.c @@ -93,6 +93,14 @@ static void apply_worker_task(void *arg) { // ─── peer table helpers ───────────────────────────────────────────────────── +int scenario_mesh_peer_count(void) { + int n = 0; + for (int i = 0; i < MESH_PEERS_MAX; i++) { + if (s_peers[i].used) n++; + } + return n; +} + esp_err_t scenario_mesh_register_peer(const char *alias, const uint8_t mac[6]) { if (!alias || !mac) return ESP_ERR_INVALID_ARG;