Skip to content

fix: use malloc() for LVGL pool on CONFIG_SPIRAM_USE_MALLOC builds - #354

Open
MuddyPaws2 wants to merge 1 commit into
meshtastic:masterfrom
MuddyPaws2:fix/lv-conf-psram-malloc-cap
Open

fix: use malloc() for LVGL pool on CONFIG_SPIRAM_USE_MALLOC builds#354
MuddyPaws2 wants to merge 1 commit into
meshtastic:masterfrom
MuddyPaws2:fix/lv-conf-psram-malloc-cap

Conversation

@MuddyPaws2

@MuddyPaws2 MuddyPaws2 commented Jul 25, 2026

Copy link
Copy Markdown

With CONFIG_SPIRAM_USE_MALLOC=y the ESP-IDF integrates PSRAM into the regular heap via the standard allocator. In this mode the PSRAM regions do NOT carry the MALLOC_CAP_SPIRAM capability, so:

heap_caps_aligned_alloc(32, size, MALLOC_CAP_SPIRAM)

returns NULL, causing LVGL's TLSF pool init to crash (null deref at offset 0x0b into the pool).

Using plain malloc() is correct: the ESP-IDF CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL threshold ensures large allocations (the pool is typically 1+ MB) go to PSRAM automatically without needing the cap.

Affects any ESP32-S3 variant built with CONFIG_SPIRAM_USE_MALLOC=y (the default for arduino-esp32 3.x / IDF 5.x boards with PSRAM).

Summary by CodeRabbit

  • Bug Fixes
    • Improved PSRAM memory allocation compatibility by using the standard memory allocator in supported configurations.
    • Helps ensure more reliable memory allocation for devices using PSRAM.

With CONFIG_SPIRAM_USE_MALLOC=y the ESP-IDF integrates PSRAM into the
regular heap via the standard allocator. In this mode the PSRAM regions
do NOT carry the MALLOC_CAP_SPIRAM capability, so:

  heap_caps_aligned_alloc(32, size, MALLOC_CAP_SPIRAM)

returns NULL, causing LVGL's TLSF pool init to crash (null deref at
offset 0x0b into the pool).

Using plain malloc() is correct: the ESP-IDF CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL
threshold ensures large allocations (the pool is typically 1+ MB) go to
PSRAM automatically without needing the cap.

Affects any ESP32-S3 variant built with CONFIG_SPIRAM_USE_MALLOC=y
(the default for arduino-esp32 3.x / IDF 5.x boards with PSRAM).
Copilot AI review requested due to automatic review settings July 25, 2026 17:52
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PSRAM heap configuration in include/lv_conf.h now uses standard malloc() instead of ESP-IDF heap-caps allocation when the built-in malloc path is enabled.

Changes

PSRAM allocator

Layer / File(s) Summary
Switch PSRAM allocation to malloc
include/lv_conf.h
The PSRAM pool includes <stdlib.h> and allocates memory with malloc(size) instead of heap_caps_aligned_alloc.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Suggested reviewers: copilot

Poem

A rabbit hops through heaps so bright,
PSRAM now mallocs with delight.
No caps to chase, no paths to roam,
Just simple bytes finding home.
Nibble, compile, and thump the floor!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: switching the LVGL pool allocator to malloc() for CONFIG_SPIRAM_USE_MALLOC builds.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Congratulations for your first pull request

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@include/lv_conf.h`:
- Around line 71-77: Update the BOARD_HAS_PSRAM allocator conditional in
include/lv_conf.h so the plain malloc() definition is used only when
CONFIG_SPIRAM_USE_MALLOC is enabled. Preserve the existing
heap_caps_aligned_alloc(..., MALLOC_CAP_SPIRAM) path for BOARD_HAS_PSRAM builds
without that configuration, while keeping the non-PSRAM allocator behavior
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a606c44e-ab36-4edb-baff-ef73db89e5ac

📥 Commits

Reviewing files that changed from the base of the PR and between ef573c3 and af49940.

📒 Files selected for processing (1)
  • include/lv_conf.h

Comment thread include/lv_conf.h
Comment on lines 71 to +77
#if defined BOARD_HAS_PSRAM
#define LV_MEM_POOL_INCLUDE <esp_heap_caps.h>
#define LV_MEM_POOL_ALLOC(size) heap_caps_aligned_alloc(32, size, MALLOC_CAP_SPIRAM)
/* CONFIG_SPIRAM_USE_MALLOC=y integrates PSRAM into the regular heap so
* MALLOC_CAP_SPIRAM capability is not set; heap_caps_aligned_alloc with
* that cap returns NULL. Use plain malloc() — large allocations route to
* PSRAM automatically. */
#define LV_MEM_POOL_INCLUDE <stdlib.h>
#define LV_MEM_POOL_ALLOC(size) malloc(size)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Repo files matching lv_conf.h:"
fd -a 'lv_conf\.h$' . || true

echo
echo "Relevant includes and config files:"
for f in $(fd -a 'lv_conf\.h$' .); do
  echo "--- $f ($(wc -l < "$f") lines) ---"
  sed -n '1,130p' "$f"
done

echo
echo "Search for MALLOC_CAP_SPIRAM / LV_MEM_POOL_ALLOC / CONFIG_SPIRAM_USE_MALLOC:"
rg -n "MALLOC_CAP_SPIRAM|LV_MEM_POOL_ALLOC|LV_MEM_POOL_INCLUDE|CONFIG_SPIRAM_USE_MALLOC|BOARD_HAS_PSRAM|heap_caps_aligned_alloc" .

Repository: meshtastic/device-ui

Length of output: 6289


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "LGFXDriver relevant lines:"
sed -n '230,295p' include/graphics/driver/LGFXDriver.h

echo
echo "Files mentioning CONFIG_SPIRAM_USE_MALLOC:"
rg -n "CONFIG_SPIRAM_USE_MALLOC|CONFIG_SPIRAM_USE_MALLOC" .

echo
echo "ESP-IDF config/platform indicators for default PSRAM allocator behavior:"
rg -n "CONFIG_SPIRAM_ALLOC_CAP|CONFIG_SPIRAM_USE_MALLOC|SPIRAM_USE_MALLOC|MALLOC_CAP_SPIRAM" -g '*.conf' -g '*.h' -g '*.c' -g '*.md' -g '*.txt' .

Repository: meshtastic/device-ui

Length of output: 4548


🌐 Web query:

ESP-IDF CONFIG_SPIRAM_USE_MALLOC heap_caps_aligned_alloc MALLOC_CAP_SPIRAM returns NULL

💡 Result:

In ESP-IDF, when CONFIG_SPIRAM_USE_MALLOC is enabled, the heap allocator adds PSRAM to the pool of memory capable of satisfying malloc() and heap_caps_malloc() requests [1][2]. If heap_caps_aligned_alloc(alignment, size, MALLOC_CAP_SPIRAM) returns NULL, it indicates that the allocator was unable to find a contiguous block in the external PSRAM that meets both the size and alignment requirements [3][4][5]. Common reasons and troubleshooting steps include: 1. PSRAM Initialization Failure: Ensure that the PSRAM is successfully initialized during boot. Check your serial logs for messages like "PSRAM initialized" or "Adding pool of... of external SPI memory to heap allocator" [6][7]. If the PSRAM is not initialized, MALLOC_CAP_SPIRAM allocations will fail [7]. 2. Alignment and Size Constraints: The heap_caps_aligned_alloc function requires a contiguous block of memory [4][5]. Even if the total free PSRAM appears sufficient, heap fragmentation may prevent the allocation of a single block of the requested size and alignment [8]. You can check the largest free block in PSRAM using heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM) to verify if the requested size can actually be satisfied [6]. 3. Misunderstanding MALLOC_CAP_SPIRAM vs. MALLOC_CAP_DEFAULT: While CONFIG_SPIRAM_USE_MALLOC allows malloc() to use PSRAM, explicitly requesting MALLOC_CAP_SPIRAM tells the allocator to only look in PSRAM [1][2]. If PSRAM is full or fragmented, this call will return NULL even if internal memory is available [3][5]. 4. Configuration Conflicts: Ensure your sdkconfig settings are correct for your specific module. Check CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL, which defines a threshold below which allocations prefer internal memory [1][9]. Also, verify that other components (like WiFi/LWIP) are not exhausting the PSRAM pool if you have enabled options like CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP [10]. To diagnose, try the following: - Use heap_caps_get_free_size(MALLOC_CAP_SPIRAM) and heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM) to see the current state of the PSRAM heap [9][6]. - If you suspect fragmentation, consider if you can use EXT_RAM_BSS_ATTR for static buffers instead of dynamic allocation [8][2]. - Verify that your alignment value is a power of two, as required by the API [4].

Citations:


Gate the malloc() allocator on CONFIG_SPIRAM_USE_MALLOC.

BOARD_HAS_PSRAM also covers builds where CONFIG_SPIRAM_USE_MALLOC is disabled, in which ESP-IDF does not automatically integrate PSRAM into the default heap. In those builds, LV_MEM_POOL_ALLOC currently routes PSRAM-backed LVGL draw buffers through malloc() instead of the existing heap_caps_aligned_alloc(..., MALLOC_CAP_SPIRAM) path, so allocations can consume internal RAM or fail.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@include/lv_conf.h` around lines 71 - 77, Update the BOARD_HAS_PSRAM allocator
conditional in include/lv_conf.h so the plain malloc() definition is used only
when CONFIG_SPIRAM_USE_MALLOC is enabled. Preserve the existing
heap_caps_aligned_alloc(..., MALLOC_CAP_SPIRAM) path for BOARD_HAS_PSRAM builds
without that configuration, while keeping the non-PSRAM allocator behavior
unchanged.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts LVGL’s memory-pool allocation on ESP32 targets with PSRAM so it doesn’t rely on MALLOC_CAP_SPIRAM in builds where PSRAM is integrated into the regular heap via CONFIG_SPIRAM_USE_MALLOC=y, preventing a NULL allocation that can crash LVGL’s pool initialization.

Changes:

  • Switch LVGL pool allocation for BOARD_HAS_PSRAM from heap_caps_aligned_alloc(..., MALLOC_CAP_SPIRAM) to a standard-heap allocation approach.
  • Update the pool allocator include accordingly.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread include/lv_conf.h
Comment on lines +72 to +77
/* CONFIG_SPIRAM_USE_MALLOC=y integrates PSRAM into the regular heap so
* MALLOC_CAP_SPIRAM capability is not set; heap_caps_aligned_alloc with
* that cap returns NULL. Use plain malloc() — large allocations route to
* PSRAM automatically. */
#define LV_MEM_POOL_INCLUDE <stdlib.h>
#define LV_MEM_POOL_ALLOC(size) malloc(size)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants