11/*
2- * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
2+ * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
33 *
44 * SPDX-License-Identifier: Apache-2.0
55 */
88#include <assert.h>
99#include <string.h>
1010#include <inttypes.h>
11+ #include <stdio.h>
1112#include <sys/mman.h>
1213#include <sys/stat.h>
1314#include <fcntl.h>
@@ -110,6 +111,64 @@ const char *esp_partition_subtype_to_str(const uint32_t type, const uint32_t sub
110111 }
111112}
112113
114+ // Calculate required emulated flash size from a partition table binary.
115+ // Returns 0 on failure.
116+ static size_t esp_partition_calc_required_flash_size_from_file (const char * partition_file_path )
117+ {
118+ if (partition_file_path == NULL || partition_file_path [0 ] == '\0' ) {
119+ return 0 ;
120+ }
121+
122+ FILE * fp = fopen (partition_file_path , "rb" );
123+ if (fp == NULL ) {
124+ return 0 ;
125+ }
126+
127+ // Determine file size as an additional lower bound
128+ if (fseek (fp , 0L , SEEK_END ) != 0 ) {
129+ fclose (fp );
130+ return 0 ;
131+ }
132+ long file_size = ftell (fp );
133+ if (file_size < 0 ) {
134+ fclose (fp );
135+ return 0 ;
136+ }
137+ if (fseek (fp , 0L , SEEK_SET ) != 0 ) {
138+ fclose (fp );
139+ return 0 ;
140+ }
141+
142+ size_t max_end = 0 ;
143+ size_t max_entries = file_size / sizeof (esp_partition_info_t );
144+ for (size_t i = 0 ; i < max_entries ; i ++ ) {
145+ esp_partition_info_t entry ;
146+ size_t r = fread (& entry , 1 , sizeof (entry ), fp );
147+ if (r != sizeof (entry ) || entry .magic != ESP_PARTITION_MAGIC ) {
148+ break ;
149+ }
150+ uint32_t end = entry .pos .offset + entry .pos .size ;
151+ if (end > max_end ) {
152+ max_end = end ;
153+ }
154+ }
155+
156+ fclose (fp );
157+
158+ // Also ensure the flash holds the partition table itself at its offset
159+ size_t min_from_table_blob = (size_t )file_size + ESP_PARTITION_TABLE_OFFSET ;
160+ size_t required = (max_end > min_from_table_blob ) ? max_end : min_from_table_blob ;
161+
162+ // Round up to emulated sector size
163+ size_t sector = ESP_PARTITION_EMULATED_SECTOR_SIZE ;
164+ size_t rem = required % sector ;
165+ if (rem != 0 ) {
166+ required += (sector - rem );
167+ }
168+
169+ return required ;
170+ }
171+
113172esp_err_t esp_partition_file_mmap (const uint8_t * * part_desc_addr_start )
114173{
115174 // temporary file is used only if control structure doesn't specify file name.
@@ -136,32 +195,24 @@ esp_err_t esp_partition_file_mmap(const uint8_t **part_desc_addr_start)
136195
137196 open_existing_file = true;
138197 } else {
139- // Open temporary file. If size was specified, also partition table has to be specified, otherwise raise error.
140- // If none of size, partition table were specified, defaults are used.
141- // Name of temporary file is available in s_esp_partition_file_mmap_ctrl.flash_file_name
142-
198+ // name of temporary file and its size is available in s_esp_partition_file_mmap_ctrl.flash_file_name and s_esp_partition_file_mmap_ctrl_input.flash_file_size respectively
143199 bool has_partfile = (strlen (s_esp_partition_file_mmap_ctrl_input .partition_file_name ) > 0 );
144200 bool has_len = (s_esp_partition_file_mmap_ctrl_input .flash_file_size > 0 );
145201
146- // conflicting input
147- if (has_partfile != has_len ) {
148- ESP_LOGE (TAG , "Invalid combination of Partition file name: %s flash file size: %" PRIu32 " was specified. Use either both parameters or none." ,
149- s_esp_partition_file_mmap_ctrl_input .partition_file_name ,
150- (uint32_t ) s_esp_partition_file_mmap_ctrl_input .flash_file_size );
151- return ESP_ERR_INVALID_ARG ;
152- }
153-
154202 // check if partition file is present, if not, use default
155203 if (!has_partfile ) {
156204 strlcpy (s_esp_partition_file_mmap_ctrl_act .partition_file_name , BUILD_DIR "/partition_table/partition-table.bin" , sizeof (s_esp_partition_file_mmap_ctrl_act .partition_file_name ));
157205 } else {
158206 strlcpy (s_esp_partition_file_mmap_ctrl_act .partition_file_name , s_esp_partition_file_mmap_ctrl_input .partition_file_name , sizeof (s_esp_partition_file_mmap_ctrl_act .partition_file_name ));
159207 }
160208
161- // check if flash size is present, if not set to default
162- if (!has_len ) {
163- s_esp_partition_file_mmap_ctrl_act .flash_file_size = ESP_PARTITION_DEFAULT_EMULATED_FLASH_SIZE ;
164- } else {
209+ // derive the partition size from the s_esp_partition_file_mmap_ctrl_act.partition_file_name
210+ size_t derived_size = esp_partition_calc_required_flash_size_from_file (s_esp_partition_file_mmap_ctrl_act .partition_file_name );
211+ // if derived size is zero, use default partition size
212+ s_esp_partition_file_mmap_ctrl_act .flash_file_size = (derived_size > 0 ) ? derived_size : ESP_PARTITION_DEFAULT_EMULATED_FLASH_SIZE ;
213+
214+ // if the size of the temporary file is specified, check if the given partition size fits within it
215+ if (has_len && s_esp_partition_file_mmap_ctrl_input .flash_file_size > derived_size ) {
165216 s_esp_partition_file_mmap_ctrl_act .flash_file_size = s_esp_partition_file_mmap_ctrl_input .flash_file_size ;
166217 }
167218
@@ -382,7 +433,7 @@ esp_err_t esp_partition_file_munmap(void)
382433
383434esp_err_t esp_partition_write (const esp_partition_t * partition , size_t dst_offset , const void * src , size_t size )
384435{
385- assert (partition != NULL && s_spiflash_mem_file_buf != NULL );
436+ assert (partition != NULL && s_spiflash_mem_file_buf != NULL && src != NULL );
386437
387438 if (partition -> readonly ) {
388439 return ESP_ERR_NOT_ALLOWED ;
@@ -397,6 +448,15 @@ esp_err_t esp_partition_write(const esp_partition_t *partition, size_t dst_offse
397448 return ESP_ERR_INVALID_SIZE ;
398449 }
399450
451+ // Ensure write stays within mapped flash file size
452+ if (s_esp_partition_file_mmap_ctrl_act .flash_file_size > 0 ) {
453+ size_t start = (size_t )partition -> address + dst_offset ;
454+ size_t max_len = s_esp_partition_file_mmap_ctrl_act .flash_file_size ;
455+ if ((start > max_len ) || ((size + start ) > max_len )) {
456+ return ESP_ERR_INVALID_SIZE ;
457+ }
458+ }
459+
400460 void * dst_addr = s_spiflash_mem_file_buf + partition -> address + dst_offset ;
401461 ESP_LOGV (TAG , "esp_partition_write(): partition=%s dst_offset=%" PRIu32 " src=%p size=%" PRIu32 " (real dst address: %p)" , partition -> label , (uint32_t ) dst_offset , src , (uint32_t ) size , dst_addr );
402462
@@ -432,7 +492,7 @@ esp_err_t esp_partition_write(const esp_partition_t *partition, size_t dst_offse
432492
433493esp_err_t esp_partition_read (const esp_partition_t * partition , size_t src_offset , void * dst , size_t size )
434494{
435- assert (partition != NULL && s_spiflash_mem_file_buf != NULL );
495+ assert (partition != NULL && s_spiflash_mem_file_buf != NULL && dst != NULL );
436496
437497 if (partition -> encrypted ) {
438498 return ESP_ERR_NOT_SUPPORTED ;
@@ -444,6 +504,15 @@ esp_err_t esp_partition_read(const esp_partition_t *partition, size_t src_offset
444504 return ESP_ERR_INVALID_SIZE ;
445505 }
446506
507+ // Ensure read stays within mapped flash file size
508+ if (s_esp_partition_file_mmap_ctrl_act .flash_file_size > 0 ) {
509+ size_t start = (size_t )partition -> address + src_offset ;
510+ size_t max_len = s_esp_partition_file_mmap_ctrl_act .flash_file_size ;
511+ if ((start > max_len ) || ((size + start ) > max_len )) {
512+ return ESP_ERR_INVALID_SIZE ;
513+ }
514+ }
515+
447516 void * src_addr = s_spiflash_mem_file_buf + partition -> address + src_offset ;
448517 ESP_LOGV (TAG , "esp_partition_read(): partition=%s src_offset=%" PRIu32 " dst=%p size=%" PRIu32 " (real src address: %p)" , partition -> label , (uint32_t ) src_offset , dst , (uint32_t ) size , src_addr );
449518
@@ -468,7 +537,7 @@ esp_err_t esp_partition_write_raw(const esp_partition_t *partition, size_t dst_o
468537
469538esp_err_t esp_partition_erase_range (const esp_partition_t * partition , size_t offset , size_t size )
470539{
471- assert (partition != NULL );
540+ assert (partition != NULL && s_spiflash_mem_file_buf != NULL );
472541
473542 if (partition -> readonly ) {
474543 return ESP_ERR_NOT_ALLOWED ;
@@ -480,6 +549,15 @@ esp_err_t esp_partition_erase_range(const esp_partition_t *partition, size_t off
480549 return ESP_ERR_INVALID_SIZE ;
481550 }
482551
552+ // Ensure erase stays within mapped flash file size
553+ if (s_esp_partition_file_mmap_ctrl_act .flash_file_size > 0 ) {
554+ size_t start = (size_t )partition -> address + offset ;
555+ size_t max_len = s_esp_partition_file_mmap_ctrl_act .flash_file_size ;
556+ if ((start > max_len ) || ((size + start ) > max_len )) {
557+ return ESP_ERR_INVALID_SIZE ;
558+ }
559+ }
560+
483561 void * target_addr = s_spiflash_mem_file_buf + partition -> address + offset ;
484562 ESP_LOGV (TAG , "esp_partition_erase_range(): partition=%s offset=%" PRIu32 " size=%" PRIu32 " (real target address: %p)" , partition -> label , (uint32_t ) offset , (uint32_t ) size , target_addr );
485563
0 commit comments