FreeRTOS runs on somewhere north of a billion devices. Medical monitors, industrial controllers, smart meters, home appliances, automotive ECUs — the list is long and the attack surface is correspondingly large. If you’re deploying FreeRTOS-based hardware and haven’t thought carefully about security configuration, you’re probably shipping devices with significant exploitable weaknesses.

The problem isn’t that FreeRTOS is insecure. It’s that the default configuration optimises for getting code running quickly, not for defending against adversaries. The security features are there — stack overflow detection, Memory Protection Unit support, task isolation — but they’re opt-in, and a lot of them require explicit configuration that doesn’t happen in most firmware projects.

This is a practical guide to what those mitigations are, why they matter, and how to enable them.

Stack Overflow Detection

Stack overflows are the most common class of memory corruption vulnerability in embedded systems, and FreeRTOS ships with built-in detection that most projects leave disabled.

There are two methods:

Method 1 (configCHECK_FOR_STACK_OVERFLOW = 1): At context switch time, FreeRTOS checks whether the stack pointer has moved beyond the end of the allocated stack region. This catches overflows that were large enough to trigger before the next context switch. It’s low overhead and catches the obvious cases.

Method 2 (configCHECK_FOR_STACK_OVERFLOW = 2): FreeRTOS fills the last 20 bytes of each task’s stack with a known pattern at creation time, and checks that the pattern is intact at every context switch. This catches slower overflows that accumulate over time without triggering the pointer check. Slightly more overhead, significantly better coverage.

Enable Method 2 and implement vApplicationStackOverflowHook(). The hook receives the task handle and name, so you can log the offending task before resetting. Don’t just silently reset — without logging, you’ll never know overflow is happening in production.

void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) {
    /* Log to persistent flash storage before reset */
    log_fault(FAULT_STACK_OVERFLOW, pcTaskName);
    NVIC_SystemReset();
}

Set configMINIMAL_STACK_SIZE conservatively and profile actual stack usage during development using uxTaskGetStackHighWaterMark(). The high water mark tells you how close to the limit each task has come. Size stacks with margin — stack usage is harder to predict than heap.

Memory Protection Unit Configuration

If your microcontroller has an MPU (Cortex-M3/M4/M7/M33 devices typically do), FreeRTOS-MPU provides hardware-enforced memory isolation between tasks. This is the most significant security primitive available on Cortex-M devices, and it’s dramatically underused.

With FreeRTOS-MPU, each task runs in an unprivileged mode with a defined set of memory regions it’s permitted to access. A compromised task cannot overwrite another task’s stack, read another task’s data, or modify kernel state. Hardware faults on illegal accesses rather than silently corrupting memory.

The configuration overhead is non-trivial — you define MPU regions for each task when you create it — but the security benefit for any device handling sensitive data or network-connected is substantial.

static StackType_t xTaskStack[256] __attribute__((aligned(256)));

static const TaskParameters_t xTaskParameters = {
    .pvTaskCode = vTaskFunction,
    .pcName = "SensorTask",
    .usStackDepth = 256,
    .pvParameters = NULL,
    .uxPriority = (1 | portPRIVILEGE_BIT),
    .puxStackBuffer = xTaskStack,
    .xRegions = {
        { ucSharedBuffer, 32, portMPU_REGION_READ_WRITE },
        { 0, 0, 0 },
        { 0, 0, 0 }
    }
};

The privilege bit on the task priority allows kernel calls. Tasks without the privilege bit are fully unprivileged — they can only access their own stack and explicitly permitted regions. Give tasks the minimum permissions they need.

Heap Security

FreeRTOS provides five heap implementations (heap_1 through heap_5). Most projects use heap_4, which supports fragmentation coalescence. None of them are security-hardened against use-after-free exploitation out of the box.

Practical mitigations:

Zero memory on allocation: Override pvPortMalloc() to zero memory before returning it. This prevents information leakage from previous allocations.

Poison freed memory: Override vPortFree() to write a known pattern (0xDEADBEEF) over freed blocks before releasing them. Use-after-free bugs will corrupt data in a recognisable way rather than silently using stale valid-looking data.

Heap integrity checks: If you’re using heap_4 or heap_5, add periodic checks of the heap block list for structural corruption. A corrupted heap usually indicates heap overflow or use-after-free.

Limit task-level allocation: Consider whether every task should be able to call pvPortMalloc(). In security-sensitive designs, restrict dynamic allocation to privileged tasks or avoid it entirely. Static allocation (using xTaskCreateStatic() and xQueueCreateStatic()) eliminates heap attacks on kernel objects entirely.

Secure Boot and Firmware Integrity

Runtime FreeRTOS hardening is undermined if an attacker can replace your firmware entirely. Secure boot roots trust in hardware and verifies firmware authenticity before execution.

On STM32 devices, the Option Bytes configure the read-out protection (RDP) level. RDP Level 1 prevents JTAG/SWD readback of flash memory. RDP Level 2 permanently disables all debug access. Set RDP Level 1 at minimum for production devices; Level 2 for high-security applications.

The STM32Trust TEE-M and similar platform security architectures provide a hardware root of trust for verification chains. On NXP devices, the HAB (High Assurance Boot) provides equivalent functionality.

If you’re using a microcontroller without hardware secure boot, implement firmware verification in a dedicated bootloader stage using a cryptographic signature check before jumping to the application. The MCUboot open-source bootloader supports FreeRTOS applications and provides image signing with ECDSA or RSA verification.

Network Security

FreeRTOS-Plus-TCP and LwIP both provide TCP/IP stacks for FreeRTOS. Neither is particularly hardened in default configuration.

TLS everywhere: Use mbedTLS (now part of the TF-M project) for any network communication. Don’t implement custom crypto. Don’t use pre-shared keys that are identical across devices — use device-unique credentials provisioned at manufacture time.

Pin certificate fingerprints rather than relying on full PKI validation chains where your CA root certificates may be missing or outdated. For devices that communicate with a known backend, pinning the expected certificate fingerprint is more robust than full chain validation.

Disable unused network services. If your device doesn’t need a web interface, don’t run one. FTP, Telnet, and debug servers have no place in production firmware.

Rate-limit incoming connections at the application level. FreeRTOS-Plus-TCP doesn’t have built-in rate limiting. Implement a simple connection counter per source address and drop connections that exceed a threshold within a time window.

Task Privilege Separation

Design your task architecture with privilege separation in mind. The common pattern of running everything in a single privileged task is convenient during development but eliminates the isolation benefits you get from a multi-task design.

A practical pattern:

  • Privileged supervisor task: handles configuration, system control, firmware updates. Minimal code, high trust.
  • Network task: unprivileged, handles inbound/outbound communications. Can only write to a shared message queue, not directly to application state.
  • Application tasks: unprivileged, process sensor data, drive actuators. Cannot directly access network buffers.
  • Sensor tasks: read-only access to peripheral registers for their specific sensor.

If a network task is compromised, it can corrupt the message queue but cannot directly write application state or system configuration. The blast radius is contained.

Logging and Intrusion Detection

FreeRTOS doesn’t have built-in security event logging. Implement your own:

Log task restarts, assertion failures, hardfaults, MPU violations, and authentication failures to a circular buffer in retained RAM (memory that survives soft resets on most Cortex-M devices). On connection to a backend, upload the log.

Monitor for anomalous task timing. If a task that normally completes in 10ms suddenly takes 500ms, something unexpected is happening — possibly exploitation triggering unexpected code paths. FreeRTOS task runtime statistics (vTaskGetRunTimeStats()) can feed into anomaly detection logic.

The EU Cyber Resilience Act and ETSI EN 303 645

If you’re shipping consumer IoT devices in the EU, the Cyber Resilience Act applies, and ETSI EN 303 645 is the baseline standard. The requirements directly map to the mitigations above: no default passwords, secure update mechanism, vulnerability disclosure policy, and minimum security by design.

The CRA doesn’t tell you to configure FreeRTOS-MPU specifically, but it does require that your device “minimise its attack surface.” An unpardoned stack overflow on a network-connected device is difficult to defend as “minimised attack surface.” Document your security configuration choices as part of your technical documentation file — auditors will ask.


FreeRTOS security isn’t a separate concern from firmware development. It’s a set of configuration decisions you make once (or don’t make, and regret later). Stack overflow detection costs you almost nothing. MPU configuration takes a day to set up correctly and buys you meaningful isolation for the product lifetime. Secure boot is genuinely the hardest to retrofit; if you’re designing new hardware, choose a microcontroller with hardware-assisted secure boot from the start.

The devices that get compromised in bulk are the ones that shipped with defaults.