控制台改单写者发送:defaultTask唯一发串口其余任务写环形缓冲,杜绝日志交错与脏指针入UART;溢出钩子加指针窗口校验、崩溃上报改显式长度

This commit is contained in:
2026-09-24 23:47:23 +08:00
parent fadec3e2b0
commit 95c5a51417
8 changed files with 346 additions and 34 deletions
+56 -14
View File
@@ -269,12 +269,21 @@ void StartDefaultTask(void *argument)
/* 配置 poll 周期计数(每 4 次循环 = 2s 轮询一次,与引擎文档/模拟器一致) */
uint32_t cfg_poll_cnt = 0u;
/* 注册控制台写者:本任务此后是串口唯一发送者(其余任务只写环形缓冲)。
* 见 Src/usart.c "控制台单写者日志"——消除并发写造成的日志逐字符交错,
* 并让脏指针无法再进入 HAL_UART_Transmit 的缓冲遍历。 */
dbg_console_writer_attach();
for (;;) {
/* 等待"保存网络配置"请求:有请求立即唤醒;无请求 500ms 后返回
* (取代原 osDelay(500):循环节奏不变,保存响应零空转) */
/* 等待"保存网络配置"或"控制台待发":任一即醒;无事件 500ms 后返回
* (取代原 osDelay(500):循环节奏不变,保存/日志响应零空转) */
if (sys_evtHandle != NULL) {
(void)osEventFlagsWait(sys_evtHandle, SYS_EVT_CFG_SAVE, osFlagsWaitAny, 500u);
(void)osEventFlagsWait(sys_evtHandle,
SYS_EVT_CFG_SAVE | SYS_EVT_CONSOLE_TX,
osFlagsWaitAny, 500u);
}
/* 控制台唯一发送点:把环形缓冲中的日志按块发出(无待发时立即返回) */
dbg_console_drain();
/* 执行待处理保存(配置引擎属主:写盘 + 成功后立即应用);无请求 = 空操作 */
#if !defined(TEST_SUITE_STORAGE)
net_cfg_save_run();
@@ -633,38 +642,71 @@ static void default_task_net_cfg_save_notify(void)
}
#endif
/*
* 名称指针合法性校验(栈溢出/越界可能已破坏 TCB,指针不可信):
* 只接受落在 Flash / 主 SRAM / CCM 映射范围内的指针;不校验就直接交给 UART,
* 钩子自身也会以同样方式崩(2026-09-24 实测 HardFault:BFAR=0x10010000 恰为
* CCM 末尾+1,PC 落在 HAL_UART_Transmit 的缓冲读取处)。
*/
static int hook_ptr_ok(const void *p)
{
uint32_t a = (uint32_t)p;
uint32_t win = 32u; /* 将要读取的最大字节数(任务名 ≤ 32) */
if ((a >= 0x08000000u) && (a <= (0x08100000u - win))) {
return 1; /* Flash(RO 区) */
}
if ((a >= 0x20000000u) && (a <= (0x20020000u - win))) {
return 1; /* SRAM1 + SRAM2 */
}
if ((a >= 0x10000000u) && (a <= (0x10010000u - win))) {
return 1; /* CCM(FreeRTOS 堆:TCB/栈所在) */
}
return 0;
}
/*
* 栈溢出钩子(configCHECK_FOR_STACK_OVERFLOW=2,2026-09-20 存储套件死机事故后加):
* 溢出 = 内核状态已不可信,禁止走 printf/内核对象,裸 UART 报任务名后复位。
* 溢出 = 内核状态已不可信,禁止走 printf/内核对象/控制台锁,裸发固定文本后复位。
* 2026-09-24 加固:任务名先经范围校验并拷入本地缓冲(不再把 TCB 指针交给 UART),
* 指针不可信时打印占位文本——避免"上报崩溃的代码自己再崩一次"。
*/
void vApplicationStackOverflowHook(void *xTask, signed char *pcTaskName)
{
static const char msg_head[] = "\r\n[ERR] STACK OVERFLOW task=";
static const char msg_bad[] = "(name unavailable)";
uint16_t len = 0u;
(void)xTask;
HAL_UART_Transmit(&huart1, (const uint8_t *)msg_head,
(uint16_t)(sizeof(msg_head) - 1u), 0xFFFFu);
if (pcTaskName != NULL) {
uint16_t len = 0;
while (len < 32u && pcTaskName[len] != '\0') {
dbg_console_write_raw(msg_head, (unsigned)(sizeof(msg_head) - 1u));
if ((pcTaskName != NULL) && (hook_ptr_ok(pcTaskName) != 0)) {
while ((len < 32u) && (pcTaskName[len] != '\0')) {
len++;
}
HAL_UART_Transmit(&huart1, (const uint8_t *)pcTaskName, len, 0xFFFFu);
}
HAL_UART_Transmit(&huart1, (const uint8_t *)"\r\n", 2u, 0xFFFFu);
if (len == 0u) {
dbg_console_write_raw(msg_bad, (unsigned)(sizeof(msg_bad) - 1u));
} else {
char name[33]; /* 拷入本地缓冲:UART 永不接触 TCB 指针 */
memcpy(name, pcTaskName, (size_t)len);
name[len] = '\0';
dbg_console_write_raw(name, (unsigned)len);
}
dbg_console_write_raw("\r\n", 2u);
HAL_Delay(200);
NVIC_SystemReset();
}
/*
* 堆分配失败钩子:只告警不复位(NULL 由各调用点自行处理并上报自检)。
* 同样走裸 UART——钩子可能在临界区内被调,printf/信号量不可用。
* 同样走裸 UART 有界发送——钩子可能在临界区内被调,printf/信号量不可用。
*/
void vApplicationMallocFailedHook(void)
{
static const char msg[] = "\r\n[ERR] MALLOC FAILED (heap exhausted)\r\n";
HAL_UART_Transmit(&huart1, (const uint8_t *)msg,
(uint16_t)(sizeof(msg) - 1u), 0xFFFFu);
dbg_console_write_raw(msg, (unsigned)(sizeof(msg) - 1u));
}
/* USER CODE END Application */