fix(sdmmc): Validate DMA buffer address range

Closes https://github.com/espressif/esp-idf/issues/18714
This commit is contained in:
Adam Múdry
2026-07-21 14:38:57 +02:00
parent 0bae8d1e27
commit a89ca4caf6
9 changed files with 106 additions and 23 deletions

View File

@@ -0,0 +1,22 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#include <stddef.h>
#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

View File

@@ -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"
@@ -95,7 +96,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;
/**
@@ -333,19 +333,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
*

View File

@@ -528,6 +528,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
@@ -536,15 +549,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;

View File

@@ -16,6 +16,7 @@ endif()
set(priv_requires "sdmmc"
"esp_driver_sdmmc"
"esp_psram"
"sdmmc_test_boards"
"common_test_flows"
"unity"

View File

@@ -6,6 +6,7 @@
#include <stdio.h>
#include <stddef.h>
#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);

View File

@@ -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`

View File

@@ -0,0 +1,49 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#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

View File

@@ -0,0 +1,2 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_IGNORE_NOTFOUND=y

View File

@@ -0,0 +1,2 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_IGNORE_NOTFOUND=y