From 7f2eca6f198d9e37fb2d7f693ccb9ff54acc633a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20M=C3=BAdry?= Date: Tue, 21 Jul 2026 14:38:57 +0200 Subject: [PATCH] fix(sdmmc): Validate DMA buffer address range Closes https://github.com/espressif/esp-idf/issues/18714 --- .../include/esp_private/sd_host_buffer.h | 22 +++++++++ .../include/esp_private/sd_host_private.h | 17 +------ .../esp_driver_sdmmc/src/sd_host_sdmmc.c | 29 ++++++++--- .../components/sdmmc_tests/CMakeLists.txt | 1 + .../components/sdmmc_tests/sdmmc_test_rw_sd.c | 5 ++ .../test_apps/sdmmc/main/CMakeLists.txt | 2 +- .../test_apps/sdmmc/main/test_sdmmc_buffer.c | 49 +++++++++++++++++++ .../test_apps/sdmmc/sdkconfig.defaults.esp32 | 2 + .../sdmmc/sdkconfig.defaults.esp32s3 | 2 + 9 files changed, 106 insertions(+), 23 deletions(-) create mode 100644 components/esp_driver_sdmmc/include/esp_private/sd_host_buffer.h create mode 100644 components/esp_driver_sdmmc/test_apps/sdmmc/main/test_sdmmc_buffer.c create mode 100644 components/esp_driver_sdmmc/test_apps/sdmmc/sdkconfig.defaults.esp32 create mode 100644 components/esp_driver_sdmmc/test_apps/sdmmc/sdkconfig.defaults.esp32s3 diff --git a/components/esp_driver_sdmmc/include/esp_private/sd_host_buffer.h b/components/esp_driver_sdmmc/include/esp_private/sd_host_buffer.h new file mode 100644 index 00000000000..00860f9fd61 --- /dev/null +++ b/components/esp_driver_sdmmc/include/esp_private/sd_host_buffer.h @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct sd_host_sdmmc_slot_t sd_host_sdmmc_slot_t; + +bool sd_host_check_buffer_alignment(sd_host_sdmmc_slot_t *slot, const void *buf, size_t size); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_sdmmc/include/esp_private/sd_host_private.h b/components/esp_driver_sdmmc/include/esp_private/sd_host_private.h index 6f0d8fcf2d0..3d4d16bec3d 100644 --- a/components/esp_driver_sdmmc/include/esp_private/sd_host_private.h +++ b/components/esp_driver_sdmmc/include/esp_private/sd_host_private.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,6 +14,7 @@ #include "esp_check.h" #include "esp_pm.h" #include "esp_cache.h" +#include "esp_private/sd_host_buffer.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "driver/sd_host_sdmmc.h" @@ -109,7 +110,6 @@ typedef struct { size_t desc_remaining; } sd_host_sdmmc_trans_state_t; -typedef struct sd_host_sdmmc_slot_t sd_host_sdmmc_slot_t; typedef struct sd_host_sdmmc_ctlr_t sd_host_sdmmc_ctlr_t; /** @@ -347,19 +347,6 @@ void sd_host_dma_prepare(sd_host_sdmmc_slot_t *slot, void* data_ptr, size_t data /*--------------------------------------------------------------- Info APIs ---------------------------------------------------------------*/ -/** - * @brief Check SD buffer alignment - * - * @param[in] slot SD Host slot handle - * @param[in] buf Buffer pointer - * @param[in] size Buffer size - * - * @return - * - True: alignment requirement is satisfied - * - False: alignment requirement is not satisfied - */ -bool sd_host_check_buffer_alignment(sd_host_sdmmc_slot_t *slot, const void *buf, size_t size); - /** * @brief Get SD Host slot real frequency * diff --git a/components/esp_driver_sdmmc/src/sd_host_sdmmc.c b/components/esp_driver_sdmmc/src/sd_host_sdmmc.c index cdd5e686e80..514f5ab469b 100644 --- a/components/esp_driver_sdmmc/src/sd_host_sdmmc.c +++ b/components/esp_driver_sdmmc/src/sd_host_sdmmc.c @@ -452,6 +452,19 @@ static esp_err_t sd_host_slot_sdmmc_get_info(sd_host_slot_handle_t slot, sd_host /*--------------------------------------------------------------- Internal APIs ---------------------------------------------------------------*/ +static bool sdmmc_dma_accessible(const void *ptr) +{ + if (esp_ptr_external_ram(ptr)) { +#if SOC_SDMMC_PSRAM_DMA_CAPABLE + return esp_ptr_dma_ext_capable(ptr); +#else + return false; +#endif + } + + return esp_ptr_dma_capable(ptr); +} + bool sd_host_check_buffer_alignment(sd_host_sdmmc_slot_t *slot, const void *buf, size_t size) { //for future-proof @@ -460,15 +473,17 @@ bool sd_host_check_buffer_alignment(sd_host_sdmmc_slot_t *slot, const void *buf, return false; } -#if !SOC_SDMMC_PSRAM_DMA_CAPABLE - // The SDMMC peripheral's DMA cannot reach PSRAM on this target, so a PSRAM - // buffer can never be used directly regardless of its alignment. Reporting - // it as not directly usable makes the protocol layer fall back to an - // internal DMA-capable buffer. - if (esp_ptr_external_ram(buf)) { + uintptr_t start = (uintptr_t) buf; + if (size - 1 > UINTPTR_MAX - start) { + return false; + } + const void *end = (const void *)(start + size - 1); + + bool not_dma_accessible = !sdmmc_dma_accessible(buf) || !sdmmc_dma_accessible(end); + bool different_memory_types = esp_ptr_external_ram(buf) != esp_ptr_external_ram(end); + if (not_dma_accessible || different_memory_types) { return false; } -#endif esp_err_t ret = ESP_FAIL; int cache_flags = 0; diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/CMakeLists.txt b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/CMakeLists.txt index 8b8b1846d50..2ef7a8b0338 100644 --- a/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/CMakeLists.txt +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/CMakeLists.txt @@ -16,6 +16,7 @@ endif() set(priv_requires "sdmmc" "esp_driver_sdmmc" + "esp_psram" "sdmmc_test_boards" "common_test_flows" "unity" diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_rw_sd.c b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_rw_sd.c index 51cc9999f7f..3e04d2ed1dc 100644 --- a/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_rw_sd.c +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_rw_sd.c @@ -6,6 +6,7 @@ #include #include #include "unity.h" +#include "esp_psram.h" #include "sdmmc_cmd.h" #include "sdmmc_test_begin_end_sd.h" #include "sdmmc_test_rw_common.h" @@ -135,6 +136,10 @@ static void do_one_sdmmc_rw_test_psram_dma_buffer(int slot, int width) int freq_khz = SDMMC_FREQ_HIGHSPEED; #if !CONFIG_SPIRAM TEST_IGNORE_MESSAGE("PSRAM is not enabled"); +#else + if (!esp_psram_is_initialized()) { + TEST_IGNORE_MESSAGE("PSRAM is not available"); + } #endif sdmmc_test_sd_skip_if_board_incompatible(slot, width, freq_khz, NO_DDR, NO_EMMC); sdmmc_test_sd_begin(slot, width, freq_khz, 0, &card); diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/main/CMakeLists.txt b/components/esp_driver_sdmmc/test_apps/sdmmc/main/CMakeLists.txt index 235dd1919ba..40a96703400 100644 --- a/components/esp_driver_sdmmc/test_apps/sdmmc/main/CMakeLists.txt +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/main/CMakeLists.txt @@ -1,4 +1,4 @@ -set(srcs "test_app_main.c" "test_sd_driver_resource.c") +set(srcs "test_app_main.c" "test_sd_driver_resource.c" "test_sdmmc_buffer.c") set(priv_requires # tests reside in this component, also available for `sdmmc_console` diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/main/test_sdmmc_buffer.c b/components/esp_driver_sdmmc/test_apps/sdmmc/main/test_sdmmc_buffer.c new file mode 100644 index 00000000000..c8e0da1d547 --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/main/test_sdmmc_buffer.c @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sdkconfig.h" +#include "unity.h" +#include "esp_private/sd_host_buffer.h" +#include "soc/soc.h" +#include "esp_heap_caps.h" +#include "esp_memory_utils.h" +#include "esp_psram.h" + +#if SOC_RTC_FAST_MEM_SUPPORTED +TEST_CASE("SDMMC rejects an RTC fast RAM DMA buffer", "[sdmmc]") +{ + const size_t buffer_size = 512; + void *internal_dma_buf = heap_caps_malloc(buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_CACHE_ALIGNED); + const size_t offset = 0x20; + const void *rtc_fast_ram_buf = (const void *)(SOC_RTC_DRAM_LOW + offset); + TEST_ASSERT_NOT_NULL(internal_dma_buf); + + TEST_ASSERT_TRUE(sd_host_check_buffer_alignment(NULL, internal_dma_buf, buffer_size)); + TEST_ASSERT_FALSE(sd_host_check_buffer_alignment(NULL, rtc_fast_ram_buf, buffer_size)); + TEST_ASSERT_FALSE(sd_host_check_buffer_alignment(NULL, (const void *)(UINTPTR_MAX - 255), buffer_size)); + + heap_caps_free(internal_dma_buf); +} +#endif + +#if CONFIG_SPIRAM && !SOC_SDMMC_PSRAM_DMA_CAPABLE +TEST_CASE("SDMMC rejects a PSRAM buffer when PSRAM is not DMA capable", "[sdmmc]") +{ + const size_t buffer_size = 512; + if (!esp_psram_is_initialized()) { + TEST_IGNORE_MESSAGE("PSRAM is not available"); + } + + void *psram_buf = heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_CACHE_ALIGNED); + TEST_ASSERT_NOT_NULL(psram_buf); + TEST_ASSERT_TRUE(esp_ptr_external_ram(psram_buf)); + + TEST_ASSERT_FALSE(sd_host_check_buffer_alignment(NULL, psram_buf, buffer_size)); + + heap_caps_free(psram_buf); +} +#endif diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/sdkconfig.defaults.esp32 b/components/esp_driver_sdmmc/test_apps/sdmmc/sdkconfig.defaults.esp32 new file mode 100644 index 00000000000..f18d2429dae --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/sdkconfig.defaults.esp32 @@ -0,0 +1,2 @@ +CONFIG_SPIRAM=y +CONFIG_SPIRAM_IGNORE_NOTFOUND=y diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/sdkconfig.defaults.esp32s3 b/components/esp_driver_sdmmc/test_apps/sdmmc/sdkconfig.defaults.esp32s3 new file mode 100644 index 00000000000..f18d2429dae --- /dev/null +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/sdkconfig.defaults.esp32s3 @@ -0,0 +1,2 @@ +CONFIG_SPIRAM=y +CONFIG_SPIRAM_IGNORE_NOTFOUND=y