espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.81k stars 7.32k forks source link

heap_caps_malloc(n, MALLOC_CAP_EXEC) returns non-executable memory on ESP32-C6 default config (IDFGH-14013) #14836

Open projectgus opened 2 weeks ago

projectgus commented 2 weeks ago

Answers checklist.

IDF version.

v5.2.3, release/v5.2 branch (v5.2.3-262-g2b35c55820)

Espressif SoC revision.

ESP32-C6FH4 (QFN32) (revision v0.1)

Operating System used.

Linux

How did you build your project?

Command line with idf.py

If you are using Windows, please specify command line type.

None

Development Kit.

ESP32-C6-DevKitM-1

Power Supply used.

USB

What is the expected behavior?

Calling heap_caps_malloc(n, MALLOC_CAP_EXEC) should return NULL or memory which is executable.

What is the actual behavior?

On ESP32-C6 with the default configuration, heap_caps_malloc(n, MALLOC_CAP_EXEC) returns a pointer to memory which is not executable and crashes on instruction fetch.

This bug seems to be fixed on master and release/v5.3 branch, but it would be great if fix could please be backported to v5.2.

Steps to reproduce.

  1. Copy the following code into an example:
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include "esp_memory_utils.h"
#include "esp_heap_caps.h"

void leak_pointer(void * arg)
{
    // Keep the compiler happy by leaking memory to here
}

void no_op(void)
{

}

void app_main(void)
{
    heap_caps_print_heap_info(MALLOC_CAP_EXEC);
    while (1) {
        void (*p)(void) = heap_caps_malloc(2048, MALLOC_CAP_EXEC);
        printf("Alloced %p\n", p);
        if (p == NULL) {
            printf("Out of executable RAM, woohoo!\n");
            return;
        }
        if (!esp_ptr_executable(p)) {
            printf("Got non-executable pointer back :(\n");
            return;
        }

        // Copy the instructions for an empty function to 'p'
        memcpy(p, no_op, 64);
        // Call the function
        p();

        leak_pointer(p);
    }
}
  1. idf.py set-target esp32c6
  2. idf.py flash monitor

Debug Logs.

I (26) boot: ESP-IDF v5.2.3-262-g2b35c55820-dirty 2nd stage bootloader
I (27) boot: compile time Nov  6 2024 15:42:35
I (28) boot: chip revision: v0.1
I (31) boot: efuse block revision: v0.2
I (36) boot.esp32c6: SPI Speed      : 80MHz
I (41) boot.esp32c6: SPI Mode       : DIO
I (46) boot.esp32c6: SPI Flash Size : 2MB
I (50) boot: Enabling RNG early entropy source...
I (56) boot: Partition Table:
I (59) boot: ## Label            Usage          Type ST Offset   Length
I (67) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (74) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (81) boot:  2 factory          factory app      00 00 00010000 00100000
I (89) boot: End of partition table
I (93) esp_image: segment 0: paddr=00010020 vaddr=42018020 size=088f4h ( 35060) map
I (115) esp_image: segment 1: paddr=0001891c vaddr=40800000 size=076fch ( 30460) load
I (130) esp_image: segment 2: paddr=00020020 vaddr=42000020 size=10260h ( 66144) map
I (156) esp_image: segment 3: paddr=00030288 vaddr=408076fc size=02e04h ( 11780) load
I (162) esp_image: segment 4: paddr=00033094 vaddr=4080a500 size=016a8h (  5800) load
I (171) boot: Loaded app from partition at offset 0x10000
I (172) boot: Disabling RNG early entropy source...
I (184) cpu_start: Unicore app
W (194) clk: esp_perip_clk_init() has not been implemented yet
I (201) cpu_start: Pro cpu start user code
I (201) cpu_start: cpu freq: 160000000 Hz
I (201) cpu_start: Application information:
I (204) cpu_start: Project name:     hello_world
I (209) cpu_start: App version:      v5.2.3-262-g2b35c55820-dirty
I (216) cpu_start: Compile time:     Nov  6 2024 15:42:30
I (222) cpu_start: ELF file SHA256:  2a4607ef8...
I (227) cpu_start: ESP-IDF:          v5.2.3-262-g2b35c55820-dirty
I (234) cpu_start: Min chip rev:     v0.0
I (239) cpu_start: Max chip rev:     v0.99 
I (244) cpu_start: Chip rev:         v0.1
I (248) heap_init: Initializing. RAM available for dynamic allocation:
I (255) heap_init: At 4080CA90 len 0006FB80 (446 KiB): RAM
I (262) heap_init: At 4087C610 len 00002F54 (11 KiB): RAM
I (268) heap_init: At 50000000 len 00003FE8 (15 KiB): RTCRAM
I (275) spi_flash: detected chip: generic
I (279) spi_flash: flash io: dio
W (283) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (296) sleep: Configure to isolate all GPIO pins in sleep state
I (303) sleep: Enable automatic switching of GPIO sleep configuration
I (310) coexist: coex firmware version: 27d8387
I (315) coexist: coexist rom version 5b8dcfa
I (320) main_task: Started on CPU0
I (320) main_task: Calling app_main()
Heap summary for capabilities 0x00000001:
  At 0x4080ca90 len 457600 free 442384 allocated 13304 min_free 442384
    largest_free_block 442368 alloc_blocks 39 free_blocks 1 total_blocks 40
  At 0x4087c610 len 12116 free 10360 allocated 0 min_free 10360
    largest_free_block 10240 alloc_blocks 0 free_blocks 1 total_blocks 1
  At 0x50000000 len 16360 free 14604 allocated 0 min_free 14604
    largest_free_block 14592 alloc_blocks 0 free_blocks 1 total_blocks 1
  Totals:
    free 467348 allocated 13304 min_free 467348 largest_free_block 442368
Alloced 0x40810604
Guru Meditation Error: Core  0 panic'ed (Instruction access fault). Exception was unhandled.

Core  0 register dump:
--- Stack dump detected
MEPC    : 0x40810604  RA      : 0x420093a0  SP      : 0x4080fbf0  GP      : 0x4080ad00  
--- 0x420093a0: app_main at /home/gus/ry/george/esp-idf-v5/examples/get-started/hello_world/main/hello_world_main.c:21

TP      : 0x4080745c  T0      : 0xc422c606  T1      : 0x50efcd05  T2      : 0x11418082  
--- 0x4080745c: xTaskCreatePinnedToCore at /home/gus/ry/george/esp-idf-v5/components/freertos/esp_additions/freertos_tasks_c_additions.h:178 (discriminator 1)

S0/FP   : 0x40810604  S1      : 0x40810604  A0      : 0x40810604  A1      : 0x4200939c  
--- 0x4200939c: app_main at /home/gus/ry/george/esp-idf-v5/examples/get-started/hello_world/main/hello_world_main.c:33

A2      : 0x80e7fdff  A3      : 0x40810644  A4      : 0x40810644  A5      : 0x40810644  
A6      : 0x0000001c  A7      : 0x40b23290  S2      : 0x00000000  S3      : 0x00000000  
S4      : 0x00000000  S5      : 0x00000000  S6      : 0x00000000  S7      : 0x00000000  
S8      : 0x00000000  S9      : 0x00000000  S10     : 0x00000000  S11     : 0x00000000  
T3      : 0x05134201  T4      : 0xb537a815  T5      : 0xb84f80ef  T6      : 0x4505c226  
MSTATUS : 0x00001881  MTVEC   : 0x40800001  MCAUSE  : 0x00000001  MTVAL   : 0x40810604  
--- 0x40800001: _vector_table at ??:?

MHARTID : 0x00000000  

--- Backtrace:

0x40810604 in ?? ()
#0  0x40810604 in ?? ()
#1  0x420093a0 in app_main () at /home/gus/ry/george/esp-idf-v5/examples/get-started/hello_world/main/hello_world_main.c:35
#2  0x00000000 in ?? ()
Backtrace stopped: frame did not save the PC

More Information.

SoucheSouche commented 1 day ago

Hi @projectgus. The problem was located in memory_layout.c. CONFIG_ESP_SYSTEM_MEMPROT_FEATURE was used to define the common caps used in soc_memory_types array instead of CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT. The fix is created and will be reviewed now. Thanks again for pointing this one out as well as for the rest of MALLOC_CAP_EXEC related tickets.