mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
feat(kasan): add Kernel Address Sanitizer (KASAN) support for ESP-IDF
Add KASAN support for detecting heap memory safety bugs (buffer overflows, underflows, use-after-free) at runtime using compiler instrumentation and shadow memory. Gated behind CONFIG_IDF_EXPERIMENTAL_FEATURES, with touch points kept to esp_system and heap so other components stay untouched. - Core runtime (esp_system/kasan.c, esp_kasan.h): nibble-based shadow memory in DRAM, poison/unpoison, per-access validation, and __asan_* stubs; hot-path stubs in IRAM so they stay valid with the flash cache off. Shadow init runs before heap bring-up. - Heap integration (heap/heap_kasan*.c): alloc/free hooks add redzones, a quarantine FIFO, and shadow updates. - Panic handling: disable checks once at the panic handler entry so backtrace and stack dumps can read redzones without nested reports. - Build system: -fsanitize=kernel-address for app code, with HAL, SoC, esp_rom, SPI flash, esp_hw_support, bootloader_support, FreeRTOS, and heap internals excluded from instrumentation. - Test app (tools/test_apps/system/kasan_test): Unity tests for overflow, underflow, use-after-free, and all sized __asan_* stubs, with halt and no-halt configurations. - Docs: document KASAN in the heap memory debugging guide (EN and CN).
This commit is contained in:
@@ -105,6 +105,12 @@ tools/test_apps/system/init_array:
|
||||
depends_filepatterns:
|
||||
- tools/tools.json
|
||||
|
||||
tools/test_apps/system/kasan_test:
|
||||
depends_components:
|
||||
- *common_components
|
||||
- esp_system
|
||||
- heap
|
||||
|
||||
tools/test_apps/system/log:
|
||||
disable_test:
|
||||
- if: IDF_TARGET not in ["esp32", "esp32c3"]
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(kasan_test)
|
||||
@@ -0,0 +1,72 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- |
|
||||
|
||||
# KASAN Test Application
|
||||
|
||||
This test application validates the Kernel Address Sanitizer (KASAN) integration
|
||||
in ESP-IDF by deliberately triggering memory safety bugs and checking that KASAN
|
||||
detects and reports them before calling the panic handler.
|
||||
|
||||
## Test Cases
|
||||
|
||||
| Test | Description | Expected Outcome |
|
||||
|------|-------------|-----------------|
|
||||
| `overflow` | 1-byte write past end of 16-byte heap allocation | KASAN WRITE error + panic |
|
||||
| `use_after_free` | Write to freed heap block | KASAN WRITE error + panic |
|
||||
| `uaf_read` | Read from freed heap block | KASAN READ error + panic |
|
||||
| `underflow` | Write before start of allocation (into left redzone) | KASAN WRITE error + panic |
|
||||
| `large_overflow` | `memset` of 16 bytes into an 8-byte buffer | KASAN WRITE error + panic |
|
||||
| `no_bug` | Clean alloc/use/free cycle | Completes without panic |
|
||||
| `asan stubs valid access no error` | Direct calls to every sized `__asan_load<N>_noabort` and `__asan_store<N>_noabort` stub (N in {1, 2, 4, 8, 16, N}) on a valid buffer | Completes without panic; covers all 12 stubs |
|
||||
| `asan stubs poisoned access all sizes` (no_halt only) | Same 12 stubs called on a freed pointer | Exactly 12 KASAN errors reported |
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
cd tools/test_apps/system/kasan_test
|
||||
idf.py set-target esp32c6
|
||||
idf.py build
|
||||
```
|
||||
|
||||
Or to select a specific test case:
|
||||
|
||||
```bash
|
||||
idf.py -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.ci.overflow" build
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
The following Kconfig options must be set (they are pre-configured in
|
||||
`sdkconfig.defaults`):
|
||||
|
||||
- `CONFIG_IDF_EXPERIMENTAL_FEATURES=y` – Required to expose KASAN in menuconfig
|
||||
- `CONFIG_COMPILER_KASAN=y` – Enable KASAN instrumentation
|
||||
- `CONFIG_ESP_TASK_WDT_EN=n` – Unity menu blocks IDLE until you press Enter or
|
||||
select a test; required for manual `idf.py monitor` as well as pytest
|
||||
|
||||
`CONFIG_COMPILER_KASAN` automatically selects `CONFIG_HEAP_USE_HOOKS`. Redzone
|
||||
size (8 bytes), quarantine size (8192 bytes), and heap poisoning (disabled) use
|
||||
their Kconfig defaults — no extra overrides are needed.
|
||||
|
||||
The `sdkconfig.ci.*` files add only mode-specific options (e.g. `CONFIG_KASAN_NO_HALT`
|
||||
for the all-in-one run, `CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT` for halt-mode pytest).
|
||||
|
||||
## Running pytest
|
||||
|
||||
```bash
|
||||
pytest pytest_kasan.py --target esp32c6 -v
|
||||
```
|
||||
|
||||
## Memory and Performance Impact
|
||||
|
||||
| Target | Shadow Memory | Code Size Overhead | Free Heap Impact |
|
||||
|--------|--------------|-------------------|-----------------|
|
||||
| ESP32 | ~42 KiB (internal SRAM) | ~1.5-3x instrumented components | ~14% of free heap |
|
||||
| ESP32-S3 | ~60 KiB | ~1.5-3x | ~16% of free heap |
|
||||
| ESP32-C3 | ~54 KiB | ~1.5-3x | ~16% of free heap |
|
||||
| ESP32-C6 | ~64 KiB | ~1.5-3x | ~14% of free heap |
|
||||
|
||||
The shadow array is placed in DRAM via the `DRAM_ATTR` attribute on
|
||||
`kasan_shadow_mem` (mapped into `dram0_data` by the standard `.dram1` linker
|
||||
mapping). On targets with PSRAM the shadow currently stays in internal SRAM;
|
||||
placing it in external RAM is not yet supported.
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "kasan_test_main.c"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES esp_system heap unity)
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*
|
||||
* Unity-based KASAN test application.
|
||||
*
|
||||
* With CONFIG_KASAN_NO_HALT: all tests run in one boot cycle; each test
|
||||
* triggers a bug and asserts that kasan_get_error_count() increased.
|
||||
*
|
||||
* Without CONFIG_KASAN_NO_HALT (default): select individual tests via the
|
||||
* Unity menu. Each error test causes an abort; pytest verifies the panic.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "unity.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_kasan.h"
|
||||
|
||||
static const char *TAG = "kasan_test";
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
TEST_CASE("heap buffer overflow", "[kasan]")
|
||||
{
|
||||
#if CONFIG_KASAN_NO_HALT
|
||||
kasan_reset_error_count();
|
||||
#endif
|
||||
char *buf = (char *)malloc(16);
|
||||
TEST_ASSERT_NOT_NULL(buf);
|
||||
memset(buf, 'A', 16);
|
||||
buf[16] = 'X'; /* write into right redzone */
|
||||
#if CONFIG_KASAN_NO_HALT
|
||||
TEST_ASSERT_GREATER_THAN(0, kasan_get_error_count());
|
||||
#endif
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
TEST_CASE("use-after-free write", "[kasan]")
|
||||
{
|
||||
#if CONFIG_KASAN_NO_HALT
|
||||
kasan_reset_error_count();
|
||||
#endif
|
||||
char *buf = (char *)malloc(32);
|
||||
TEST_ASSERT_NOT_NULL(buf);
|
||||
memset(buf, 0, 32);
|
||||
free(buf);
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wuse-after-free"
|
||||
buf[4] = 'Y';
|
||||
#pragma GCC diagnostic pop
|
||||
#if CONFIG_KASAN_NO_HALT
|
||||
TEST_ASSERT_GREATER_THAN(0, kasan_get_error_count());
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
TEST_CASE("use-after-free read", "[kasan]")
|
||||
{
|
||||
#if CONFIG_KASAN_NO_HALT
|
||||
kasan_reset_error_count();
|
||||
#endif
|
||||
int *buf = (int *)malloc(4 * sizeof(int));
|
||||
TEST_ASSERT_NOT_NULL(buf);
|
||||
buf[0] = 42;
|
||||
free(buf);
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wuse-after-free"
|
||||
volatile int val = buf[0];
|
||||
(void)val;
|
||||
#pragma GCC diagnostic pop
|
||||
#if CONFIG_KASAN_NO_HALT
|
||||
TEST_ASSERT_GREATER_THAN(0, kasan_get_error_count());
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
TEST_CASE("heap buffer underflow", "[kasan]")
|
||||
{
|
||||
#if CONFIG_KASAN_NO_HALT
|
||||
kasan_reset_error_count();
|
||||
#endif
|
||||
char *buf = (char *)malloc(16);
|
||||
TEST_ASSERT_NOT_NULL(buf);
|
||||
buf[-1] = 'Z'; /* write into left redzone */
|
||||
#if CONFIG_KASAN_NO_HALT
|
||||
TEST_ASSERT_GREATER_THAN(0, kasan_get_error_count());
|
||||
/* The underflow write corrupted the redzone header; skip free to avoid
|
||||
* side-effects from a bad header read. Small intentional leak. */
|
||||
#else
|
||||
free(buf);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
TEST_CASE("large heap overflow", "[kasan]")
|
||||
{
|
||||
#if CONFIG_KASAN_NO_HALT
|
||||
kasan_reset_error_count();
|
||||
#endif
|
||||
char *buf = (char *)malloc(8);
|
||||
TEST_ASSERT_NOT_NULL(buf);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
buf[i] = 0;
|
||||
}
|
||||
buf[8] = 'X'; /* overflow into right redzone */
|
||||
#if CONFIG_KASAN_NO_HALT
|
||||
TEST_ASSERT_GREATER_THAN(0, kasan_get_error_count());
|
||||
#endif
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
TEST_CASE("no false positive", "[kasan]")
|
||||
{
|
||||
#if CONFIG_KASAN_NO_HALT
|
||||
kasan_reset_error_count();
|
||||
#endif
|
||||
char *buf = (char *)malloc(32);
|
||||
TEST_ASSERT_NOT_NULL(buf);
|
||||
memset(buf, 'A', 32);
|
||||
volatile char c = buf[31];
|
||||
(void)c;
|
||||
free(buf);
|
||||
#if CONFIG_KASAN_NO_HALT
|
||||
TEST_ASSERT_EQUAL_UINT32(0, kasan_get_error_count());
|
||||
#endif
|
||||
ESP_LOGI(TAG, "no-bug test PASSED");
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/*
|
||||
* GCC -fsanitize=kernel-address instrumentation calls a sized family of
|
||||
* stubs: __asan_load<N>_noabort and __asan_store<N>_noabort for
|
||||
* N in {1, 2, 4, 8, 16, N}. The two tests below exercise every stub
|
||||
* directly so the runtime contract (link symbol present, valid access
|
||||
* passes, poisoned access reports an error) is verified for each size.
|
||||
*
|
||||
* Calling the stubs directly is intentional: GCC's choice of which sized
|
||||
* stub to emit depends on access size, alignment, and target ISA, so
|
||||
* relying on instrumentation alone leaves gaps (this is exactly how the
|
||||
* 16-byte variants were missed previously: the dedicated test code
|
||||
* never tripped GCC into emitting `__asan_*16_noabort`).
|
||||
*/
|
||||
|
||||
extern void __asan_load1_noabort(void *addr);
|
||||
extern void __asan_load2_noabort(void *addr);
|
||||
extern void __asan_load4_noabort(void *addr);
|
||||
extern void __asan_load8_noabort(void *addr);
|
||||
extern void __asan_load16_noabort(void *addr);
|
||||
/*
|
||||
* The size parameter for the variable-size ASAN check stubs is declared as
|
||||
* `int` by GCC's builtin, so we have to match that type here even though
|
||||
* the size in practice is non-negative. Using `size_t` would trigger
|
||||
* -Werror=builtin-declaration-mismatch under newer GCC (15+).
|
||||
*/
|
||||
extern void __asan_loadN_noabort(void *addr, int size);
|
||||
|
||||
extern void __asan_store1_noabort(void *addr);
|
||||
extern void __asan_store2_noabort(void *addr);
|
||||
extern void __asan_store4_noabort(void *addr);
|
||||
extern void __asan_store8_noabort(void *addr);
|
||||
extern void __asan_store16_noabort(void *addr);
|
||||
extern void __asan_storeN_noabort(void *addr, int size);
|
||||
|
||||
/* All 12 sized ASAN check stubs in one call set.
|
||||
* Returns the number of stub calls made (always 12). */
|
||||
static unsigned exercise_all_asan_stubs(void *p)
|
||||
{
|
||||
__asan_load1_noabort(p);
|
||||
__asan_load2_noabort(p);
|
||||
__asan_load4_noabort(p);
|
||||
__asan_load8_noabort(p);
|
||||
__asan_load16_noabort(p);
|
||||
__asan_loadN_noabort(p, 7);
|
||||
|
||||
__asan_store1_noabort(p);
|
||||
__asan_store2_noabort(p);
|
||||
__asan_store4_noabort(p);
|
||||
__asan_store8_noabort(p);
|
||||
__asan_store16_noabort(p);
|
||||
__asan_storeN_noabort(p, 7);
|
||||
|
||||
return 12;
|
||||
}
|
||||
|
||||
TEST_CASE("asan stubs valid access no error", "[kasan]")
|
||||
{
|
||||
#if CONFIG_KASAN_NO_HALT
|
||||
kasan_reset_error_count();
|
||||
#endif
|
||||
/* malloc(64) gives us 64 valid bytes; the largest stub reads 16 bytes
|
||||
* starting at p, so p..p+63 is safely inside the allocation. */
|
||||
char *buf = (char *)malloc(64);
|
||||
TEST_ASSERT_NOT_NULL(buf);
|
||||
memset(buf, 'A', 64);
|
||||
|
||||
unsigned calls = exercise_all_asan_stubs(buf);
|
||||
TEST_ASSERT_EQUAL_UINT(12, calls);
|
||||
|
||||
#if CONFIG_KASAN_NO_HALT
|
||||
/* Each stub touched only valid bytes; no error should have been raised. */
|
||||
TEST_ASSERT_EQUAL_UINT32(0, kasan_get_error_count());
|
||||
#endif
|
||||
free(buf);
|
||||
ESP_LOGI(TAG, "asan stubs valid-access test PASSED");
|
||||
}
|
||||
|
||||
#if CONFIG_KASAN_NO_HALT
|
||||
TEST_CASE("asan stubs poisoned access all sizes", "[kasan]")
|
||||
{
|
||||
kasan_reset_error_count();
|
||||
|
||||
/* A freed pointer sits in the quarantine FIFO with its full block
|
||||
* shadow poisoned, so every stub size will trip the shadow check. */
|
||||
char *buf = (char *)malloc(64);
|
||||
TEST_ASSERT_NOT_NULL(buf);
|
||||
memset(buf, 0, 64);
|
||||
free(buf);
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wuse-after-free"
|
||||
unsigned calls = exercise_all_asan_stubs(buf);
|
||||
#pragma GCC diagnostic pop
|
||||
TEST_ASSERT_EQUAL_UINT(12, calls);
|
||||
|
||||
/* Exactly one error per stub call: 6 sized loads + 6 sized stores. */
|
||||
TEST_ASSERT_EQUAL_UINT32(12, kasan_get_error_count());
|
||||
ESP_LOGI(TAG, "asan stubs poisoned-access test PASSED (12 errors)");
|
||||
}
|
||||
#endif /* CONFIG_KASAN_NO_HALT */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "KASAN test application starting");
|
||||
unity_run_menu();
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
"""
|
||||
Pytest test cases for the KASAN Unity test application.
|
||||
|
||||
Two configurations:
|
||||
- no_halt: all tests run in one boot cycle (CONFIG_KASAN_NO_HALT=y).
|
||||
The Unity runner executes every test; each verifies the
|
||||
KASAN error count.
|
||||
- halt: each error test triggers an abort. pytest selects one test
|
||||
at a time via the Unity menu, expecting a panic.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
from pytest_embedded_idf.utils import idf_parametrize
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# no_halt configuration: all tests pass in one run
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@idf_parametrize('target', ['supported_targets', 'preview_targets'], indirect=['target'])
|
||||
@pytest.mark.parametrize('config', ['no_halt'], indirect=True)
|
||||
def test_kasan_no_halt_all(dut: Dut) -> None:
|
||||
"""Run all KASAN tests in one boot cycle with CONFIG_KASAN_NO_HALT=y."""
|
||||
dut.expect('KASAN test application starting', timeout=15)
|
||||
# Send '*' to Unity menu to run all tests
|
||||
dut.write('*')
|
||||
dut.expect(r'\d+ Tests \d+ Failures \d+ Ignored', timeout=45)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# halt configuration: each error test causes an abort
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _run_halt_test(dut: Dut, test_name: str) -> None:
|
||||
"""Select a test from Unity menu and expect KASAN abort."""
|
||||
dut.expect('KASAN test application starting', timeout=15)
|
||||
dut.write('"' + test_name + '"')
|
||||
dut.expect(r'KASAN error: (WRITE|READ) of size \d+ at 0x', timeout=20)
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.timeout(120)
|
||||
@idf_parametrize('target', ['supported_targets', 'preview_targets'], indirect=['target'])
|
||||
@pytest.mark.parametrize('config', ['halt'], indirect=True)
|
||||
def test_kasan_halt_overflow(dut: Dut) -> None:
|
||||
_run_halt_test(dut, 'heap buffer overflow')
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.timeout(120)
|
||||
@idf_parametrize('target', ['supported_targets', 'preview_targets'], indirect=['target'])
|
||||
@pytest.mark.parametrize('config', ['halt'], indirect=True)
|
||||
def test_kasan_halt_uaf_write(dut: Dut) -> None:
|
||||
_run_halt_test(dut, 'use-after-free write')
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.timeout(120)
|
||||
@idf_parametrize('target', ['supported_targets', 'preview_targets'], indirect=['target'])
|
||||
@pytest.mark.parametrize('config', ['halt'], indirect=True)
|
||||
def test_kasan_halt_uaf_read(dut: Dut) -> None:
|
||||
_run_halt_test(dut, 'use-after-free read')
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.timeout(120)
|
||||
@idf_parametrize('target', ['supported_targets', 'preview_targets'], indirect=['target'])
|
||||
@pytest.mark.parametrize('config', ['halt'], indirect=True)
|
||||
def test_kasan_halt_underflow(dut: Dut) -> None:
|
||||
_run_halt_test(dut, 'heap buffer underflow')
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.timeout(120)
|
||||
@idf_parametrize('target', ['supported_targets', 'preview_targets'], indirect=['target'])
|
||||
@pytest.mark.parametrize('config', ['halt'], indirect=True)
|
||||
def test_kasan_halt_large_overflow(dut: Dut) -> None:
|
||||
_run_halt_test(dut, 'large heap overflow')
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@idf_parametrize('target', ['supported_targets', 'preview_targets'], indirect=['target'])
|
||||
@pytest.mark.parametrize('config', ['halt'], indirect=True)
|
||||
def test_kasan_halt_no_false_positive(dut: Dut) -> None:
|
||||
"""No-bug test should complete without KASAN error."""
|
||||
dut.expect('KASAN test application starting', timeout=15)
|
||||
dut.write('"no false positive"')
|
||||
dut.expect('no-bug test PASSED', timeout=15)
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@idf_parametrize('target', ['supported_targets', 'preview_targets'], indirect=['target'])
|
||||
@pytest.mark.parametrize('config', ['halt'], indirect=True)
|
||||
def test_kasan_halt_asan_stubs_valid_access(dut: Dut) -> None:
|
||||
"""Direct calls to every sized __asan_*_noabort stub on a valid buffer
|
||||
must link and run without raising an error."""
|
||||
dut.expect('KASAN test application starting', timeout=15)
|
||||
dut.write('"asan stubs valid access no error"')
|
||||
dut.expect('asan stubs valid-access test PASSED', timeout=15)
|
||||
@@ -0,0 +1,2 @@
|
||||
# Halt on panic so pytest can read the register dump (not KASAN-specific).
|
||||
CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y
|
||||
@@ -0,0 +1,2 @@
|
||||
# Run all tests in one boot cycle without aborting on errors
|
||||
CONFIG_KASAN_NO_HALT=y
|
||||
@@ -0,0 +1,6 @@
|
||||
# Minimal KASAN enablement — everything else stays at Kconfig defaults
|
||||
# (redzone=8, quarantine=8192, heap poisoning=disabled, HEAP_USE_HOOKS selected).
|
||||
|
||||
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
|
||||
CONFIG_COMPILER_KASAN=y
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
||||
Reference in New Issue
Block a user