Skip to content

Commit 2c72223

Browse files
committed
Merge branch 'bugfix/protect_spiflash_regions' into 'master'
spi_flash: Abort on writes to dangerous regions (bootloader, partition table, app) See merge request !1452
2 parents abacf8d + 670733d commit 2c72223

File tree

4 files changed

+104
-0
lines changed

4 files changed

+104
-0
lines changed

components/spi_flash/Kconfig

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,31 @@ config SPI_FLASH_ROM_DRIVER_PATCH
2121
This option is needed to write to flash on ESP32-D2WD, and any configuration
2222
where external SPI flash is connected to non-default pins.
2323

24+
choice SPI_FLASH_WRITING_DANGEROUS_REGIONS
25+
bool "Writing to dangerous flash regions"
26+
default SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS
27+
help
28+
SPI flash APIs can optionally abort or return a failure code
29+
if erasing or writing addresses that fall at the beginning
30+
of flash (covering the bootloader and partition table) or that
31+
overlap the app partition that contains the running app.
32+
33+
It is not recommended to ever write to these regions from an IDF app,
34+
and this check prevents logic errors or corrupted firmware memory from
35+
damaging these regions.
36+
37+
Note that this feature *does not* check calls to the esp_rom_xxx SPI flash
38+
ROM functions. These functions should not be called directly from IDF
39+
applications.
40+
41+
config SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS
42+
bool "Aborts"
43+
config SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS
44+
bool "Fails"
45+
config SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED
46+
bool "Allowed"
47+
endchoice
48+
2449
endmenu
2550

2651

components/spi_flash/flash_ops.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#include "esp_spi_flash.h"
3232
#include "esp_log.h"
3333
#include "esp_clk.h"
34+
#include "esp_flash_partitions.h"
35+
#include "esp_ota_ops.h"
3436
#include "cache_utils.h"
3537

3638
/* bytes erased by SPIEraseBlock() ROM function */
@@ -83,6 +85,45 @@ const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_no_os_ops = {
8385

8486
static const spi_flash_guard_funcs_t *s_flash_guard_ops;
8587

88+
#ifdef CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS
89+
#define UNSAFE_WRITE_ADDRESS abort()
90+
#else
91+
#define UNSAFE_WRITE_ADDRESS return false
92+
#endif
93+
94+
95+
/* CHECK_WRITE_ADDRESS macro to fail writes which land in the
96+
bootloader, partition table, or running application region.
97+
*/
98+
#if CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED
99+
#define CHECK_WRITE_ADDRESS(ADDR, SIZE)
100+
#else /* FAILS or ABORTS */
101+
#define CHECK_WRITE_ADDRESS(ADDR, SIZE) do { \
102+
if (!is_safe_write_address(ADDR, SIZE)) { \
103+
return ESP_ERR_INVALID_ARG; \
104+
} \
105+
} while(0)
106+
#endif // CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED
107+
108+
static __attribute__((unused)) bool is_safe_write_address(size_t addr, size_t size)
109+
{
110+
bool result = true;
111+
if (addr <= ESP_PARTITION_TABLE_OFFSET + ESP_PARTITION_TABLE_MAX_LEN) {
112+
UNSAFE_WRITE_ADDRESS;
113+
}
114+
115+
const esp_partition_t *p = esp_ota_get_running_partition();
116+
if (addr >= p->address && addr < p->address + p->size) {
117+
UNSAFE_WRITE_ADDRESS;
118+
}
119+
if (addr < p->address && addr + size > p->address) {
120+
UNSAFE_WRITE_ADDRESS;
121+
}
122+
123+
return result;
124+
}
125+
126+
86127
void spi_flash_init()
87128
{
88129
spi_flash_init_lock();
@@ -146,11 +187,13 @@ static esp_rom_spiflash_result_t IRAM_ATTR spi_flash_unlock()
146187

147188
esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec)
148189
{
190+
CHECK_WRITE_ADDRESS(sec * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE);
149191
return spi_flash_erase_range(sec * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE);
150192
}
151193

152194
esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size)
153195
{
196+
CHECK_WRITE_ADDRESS(start_addr, size);
154197
if (start_addr % SPI_FLASH_SEC_SIZE != 0) {
155198
return ESP_ERR_INVALID_ARG;
156199
}
@@ -187,6 +230,7 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size)
187230

188231
esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
189232
{
233+
CHECK_WRITE_ADDRESS(dst, size);
190234
// Out of bound writes are checked in ROM code, but we can give better
191235
// error code here
192236
if (dst + size > g_rom_flashchip.chip_size) {
@@ -281,6 +325,7 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
281325

282326
esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src, size_t size)
283327
{
328+
CHECK_WRITE_ADDRESS(dest_addr, size);
284329
const uint8_t *ssrc = (const uint8_t *)src;
285330
if ((dest_addr % 16) != 0) {
286331
return ESP_ERR_INVALID_ARG;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#include <string.h>
2+
3+
#include "unity.h"
4+
#include "esp_spi_flash.h"
5+
#include "esp_ota_ops.h"
6+
7+
#if CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS || CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS
8+
9+
static const char *data = "blah blah blah";
10+
11+
#if CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS
12+
#define TEST_TAGS "[spi_flash]"
13+
#else // ABORTS
14+
#define TEST_TAGS "[spi_flash][ignore]"
15+
#endif
16+
17+
TEST_CASE("can't overwrite bootloader", TEST_TAGS)
18+
{
19+
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_write(0x1000, data, strlen(data)));
20+
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_write(0x0FF8, data, strlen(data)));
21+
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_write(0x1400, data, strlen(data)));
22+
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_erase_range(0x8000, 0x2000));
23+
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_erase_range(0x7000, 0x2000));
24+
}
25+
26+
TEST_CASE("can't overwrite current running app", TEST_TAGS)
27+
{
28+
const esp_partition_t *p = esp_ota_get_running_partition();
29+
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_write(p->address + 1024, data, strlen(data)));
30+
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_flash_erase_range(p->address + 4096, 8192));
31+
}
32+
33+
#endif // FAILS || ABORTS

tools/unit-test-app/sdkconfig.defaults

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ CONFIG_MBEDTLS_HARDWARE_SHA=y
1919
CONFIG_SPI_FLASH_ENABLE_COUNTERS=y
2020
CONFIG_ULP_COPROC_ENABLED=y
2121
CONFIG_TASK_WDT=n
22+
CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS=y

0 commit comments

Comments
 (0)