SHT30 온습도 모니터링 시스템 전체 소스(서버 PHP, STM32 펌웨어, SQL, 테스트). 전체 코드리뷰에서 도출된 보안 하드닝 10건 반영: - 요청 서명 HMAC-SHA256 전환(펌웨어 sig.c/서버 config.php/호스트 패리티 동시) - 재전송 방어 + 기본 API_KEY fail-closed + 디바이스 문자열 정제(api/sensor_data.php) - 오프라인 SMS 중복 발송 경합 제거(cron_heartbeat.php, 원자적 선점) - CSV 수식 주입 방지(monthly_report.php), 감사로그 회전 락(retention_cleanup.php) - 브루트포스 카운터 원자화(login.php), 예시 TOTP 비밀키 무효화, 마이그레이션 멱등화 _backup/(하드코딩 실 비밀값 포함)·config.local.php·런타임 상태는 .gitignore 제외.
121 lines
5.9 KiB
C
121 lines
5.9 KiB
C
/* =============================================================================
|
|
* rtos_hooks.c - FreeRTOS 후크/정적 메모리 제공자
|
|
*
|
|
* FreeRTOSConfig.h 에서 켜진 후크들을 구현한다:
|
|
* - configCHECK_FOR_STACK_OVERFLOW=2 -> vApplicationStackOverflowHook
|
|
* - configUSE_MALLOC_FAILED_HOOK=1 -> vApplicationMallocFailedHook
|
|
* - configUSE_IDLE_HOOK=1 -> vApplicationIdleHook (IWDG refresh)
|
|
* - configSUPPORT_STATIC_ALLOCATION=1-> vApplicationGetIdleTaskMemory /
|
|
* vApplicationGetTimerTaskMemory
|
|
* - configASSERT -> vAssertCalled
|
|
*
|
|
* 치명적 오류(스택 오버플로/malloc 실패/assert) 처리 전략:
|
|
* 인터럽트를 끄고, 디버거가 붙어 있으면 정지(BKPT), 아니면 무한 루프에 머문다.
|
|
* 이때 IWDG refresh 가 멈추므로 워치독이 MCU 를 안전하게 리셋한다.
|
|
* 가능하면 bsp_fatal() 로 LED 점멸/로그를 남긴다(약결합 weak fallback 제공).
|
|
* ===========================================================================*/
|
|
|
|
#include "FreeRTOS.h"
|
|
#include "task.h"
|
|
#include "timers.h"
|
|
|
|
#include <stdint.h>
|
|
|
|
#include "bsp.h" /* bsp_fatal(const char*) — 안전한 오류 표시기 */
|
|
#include "watchdog.h" /* watchdog_refresh() — idle 후크에서 호출 */
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* bsp_fatal 약결합 fallback.
|
|
* bsp.c 가 실제 구현(LED 점멸 + 디버그 정지)을 제공한다. 단위/호스트 테스트나
|
|
* bsp 미링크 환경에서도 본 파일이 단독으로 빌드되도록 weak 기본 구현을 둔다.
|
|
* 실제 펌웨어 링크 시에는 bsp.c 의 강한 심볼이 이 정의를 대체한다.
|
|
* ---------------------------------------------------------------------------*/
|
|
__attribute__((weak)) void bsp_fatal(const char *reason)
|
|
{
|
|
(void)reason;
|
|
|
|
/* 모든 인터럽트 차단 (FromISR 경로 포함). 이후 워치독만이 복구 경로. */
|
|
taskDISABLE_INTERRUPTS();
|
|
|
|
#if defined(__GNUC__) && (defined(__ARM_ARCH) || defined(__arm__))
|
|
/* 디버거가 붙어 있으면 정지(C_DEBUGEN 비트). 아니면 그냥 통과 후 루프. */
|
|
volatile uint32_t *const dhcsr = (volatile uint32_t *)0xE000EDF0u;
|
|
if ((*dhcsr & 0x1u) != 0u) {
|
|
__asm volatile ("bkpt #0");
|
|
}
|
|
#endif
|
|
|
|
/* 무한 루프 — watchdog_refresh() 가 호출되지 않으므로 IWDG 가 리셋시킨다. */
|
|
for (;;) {
|
|
/* 의도적으로 비움. */
|
|
}
|
|
}
|
|
|
|
/* =============================================================================
|
|
* 스택 오버플로 후크 (configCHECK_FOR_STACK_OVERFLOW=2)
|
|
* 스케줄러가 컨텍스트 스위치 시 스택 한계를 넘었다고 판단하면 호출한다.
|
|
* 태스크 이름을 reason 으로 넘겨 bsp_fatal 로 이관.
|
|
* ===========================================================================*/
|
|
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
|
|
{
|
|
(void)xTask;
|
|
bsp_fatal(pcTaskName ? pcTaskName : "stack-overflow");
|
|
}
|
|
|
|
/* =============================================================================
|
|
* malloc 실패 후크 (configUSE_MALLOC_FAILED_HOOK=1)
|
|
* heap_4 의 pvPortMalloc 이 실패하면 호출. 힙 고갈/단편화 -> 안전 리셋.
|
|
* ===========================================================================*/
|
|
void vApplicationMallocFailedHook(void)
|
|
{
|
|
bsp_fatal("malloc-failed");
|
|
}
|
|
|
|
/* =============================================================================
|
|
* Idle 후크 (configUSE_IDLE_HOOK=1)
|
|
* Idle 태스크가 돌 때마다 호출. 모든 애플리케이션 태스크가 정상적으로 양보하여
|
|
* Idle 까지 도달했다는 신호이므로 여기서 IWDG 를 갱신한다.
|
|
* (헬스 루프 데드락/기아 상태면 Idle 에 도달하지 못해 워치독이 리셋.)
|
|
*
|
|
* 주의: Idle 후크 안에서는 블로킹 API(vTaskDelay 등)를 호출하면 안 된다.
|
|
* watchdog_refresh() 는 IWDG_KR 레지스터 한 번 쓰기로 논블로킹.
|
|
* ===========================================================================*/
|
|
void vApplicationIdleHook(void)
|
|
{
|
|
watchdog_refresh();
|
|
/* WFI 로 코어를 잠재워 소비전력/발열을 줄인다(다음 인터럽트까지 대기). */
|
|
__asm volatile ("wfi");
|
|
}
|
|
|
|
/* =============================================================================
|
|
* configASSERT 처리기
|
|
* 파일/라인 정보를 보존하기 위해 지역 정적에 저장한 뒤 bsp_fatal 로 정지.
|
|
* ===========================================================================*/
|
|
void vAssertCalled(const char *pcFile, unsigned long ulLine)
|
|
{
|
|
/* 디버거에서 검사할 수 있도록 컨텍스트 보존(최적화 제거 방지: volatile). */
|
|
static volatile const char *file;
|
|
static volatile unsigned long line;
|
|
file = pcFile;
|
|
line = ulLine;
|
|
(void)file;
|
|
(void)line;
|
|
|
|
bsp_fatal("assert");
|
|
}
|
|
|
|
/* =============================================================================
|
|
* 정적 할당 콜백 (Idle/Timer 태스크 메모리)
|
|
*
|
|
* FreeRTOSConfig.h 는 configSUPPORT_STATIC_ALLOCATION=1 +
|
|
* configKERNEL_PROVIDED_STATIC_MEMORY=1 (FreeRTOS-Kernel V11.1.0, 벤더 고정)로
|
|
* 설정되어 있다. 이 조합에서 커널의 tasks.c/timers.c 가 vApplicationGetIdleTaskMemory
|
|
* / vApplicationGetTimerTaskMemory 를 **강한(non-weak) 심볼로 직접 제공**한다.
|
|
* 따라서 여기서 같은 함수를 정의하면 다중 정의(multiple definition) 링크 오류가 난다.
|
|
* -> 이 파일에서는 두 콜백을 정의하지 않는다(커널이 소유).
|
|
*
|
|
* 만약 더 낮은 커널(예: V10.x, configKERNEL_PROVIDED_STATIC_MEMORY 미지원)으로
|
|
* 교체하면 위 두 콜백이 미정의(undefined reference)가 되므로, 그때 이 자리에
|
|
* 명시적 제공자를 다시 추가한다. (커널 프로토타입의 stack-size 인자 타입은
|
|
* configSTACK_DEPTH_TYPE* 임에 유의.)
|
|
* ===========================================================================*/
|