Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixing OTA write up to SPI_FLASH_SEC_SIZE margins (IDFGH-11311) #12460

Closed
wants to merge 1 commit into from

Conversation

kohait00
Copy link
Contributor

@kohait00 kohait00 commented Oct 25, 2023

when OTA update is performed on a partition that extends to the very last byte of the flash, then ...
esp_partition_erase_range, called by esp_ota_write(), will get to erase the sector past flash size which it will decline with an error.

example:

120 sectors to write, (0 to 119), sector 120 is just outside the flash
writing the last bytes <= SPI_FLASH_SEC_SIZE will produce this:

first_sector will be 119 (already erased in previous write operations, has a ramainder to write)
last_sector will be 120 (just outside flash)

the fix determines this situation (condition could probably even better be
if((it->wrote_size + size) >= it->part->size)

and repositions the last_sector to really be the last, so when it is compared to be == first_sector, it will be ignored..

I also believe, that esp_partition_erase_range has a latent issue

...
esp_err_t esp_partition_erase_range(const esp_partition_t *partition,
size_t offset, size_t size)
{
assert(partition != NULL);
if (offset > partition->size) { //<<----------- should be >=
return ESP_ERR_INVALID_ARG;
}
if (offset + size > partition->size) { //<<----------- should be >=
return ESP_ERR_INVALID_SIZE;
}
...

but I probably didnt get all the internal info right...

@github-actions
Copy link

github-actions bot commented Oct 25, 2023

Messages
📖 Good Job! All checks are passing!

👋 Welcome kohait00, thank you for your first contribution to espressif/esp-idf project!

📘 Please check Contributions Guide for the contribution checklist, information regarding code and documentation style, testing and other topics.

🖊️ Please also make sure you have read and signed the Contributor License Agreement for espressif/esp-idf project.

Pull request review and merge process you can expect

Espressif develops the ESP-IDF project in an internal repository (Gitlab). We do welcome contributions in the form of bug reports, feature requests and pull requests via this public GitHub repository.

  1. An internal issue has been created for the PR, we assign it to the relevant engineer
  2. They review the PR and either approve it or ask you for changes or clarifications
  3. Once the Github PR is approved, we synchronize it into our internal git repository
  4. In the internal git repository we do the final review, collect approvals from core owners and make sure all the automated tests are passing
    • At this point we may do some adjustments to the proposed change, or extend it by adding tests or documentation.
  5. If the change is approved and passes the tests it is merged into the master branch
  6. On next sync from the internal git repository merged change will appear in this public Github repository

Generated by 🚫 dangerJS against 61699ba

@espressif-bot espressif-bot added the Status: Opened Issue is new label Oct 25, 2023
@github-actions github-actions bot changed the title fixing OTA on partition just at the end of the flash fixing OTA on partition just at the end of the flash (IDFGH-11311) Oct 25, 2023
@mahavirj
Copy link
Member

120 sectors to write, (0 to 119), sector 120 is just outside the flash
it->wrote-size is some bytes < SPI_FLASH_SEC_SIZE short of flash end, then
first_sector will be 119 (already erased in previous write operations, has a ramainder to write)
last_sector will be 120 (outside flash)

Sorry but I fail to understand the fix here. If the data goes above the specified partition size then erase is likely to fail. Could you please create maybe a small application with restricted flash size and partition table to reproduce the problem? That will help to understand this scenario.

I also believe, that esp_partition_erase_range has a latent issue
...
esp_err_t esp_partition_erase_range(const esp_partition_t *partition,
size_t offset, size_t size)
{
assert(partition != NULL);
if (offset > partition->size) { //<<----------- should be >=
return ESP_ERR_INVALID_ARG;
}
if (offset + size > partition->size) { //<<----------- should be >=
return ESP_ERR_INVALID_SIZE;
}
...

Wouldn't this cause a problem for a case where the partition size is 8K and input arguments are offset = 4K, size = 4K?

@kohait00
Copy link
Contributor Author

I try to show it by a printf output..

esp_ota_ops.c:211 (esp_ota_write())

  •            ESP_LOGW(TAG, "OTA image wrote_size = %X, size = %X, wrote_size+size = %X (first 0x%02X, last 0x%02X)",
    
  •            		(int)it->wrote_size, (int)size, (int)it->wrote_size+size, (int)first_sector, (int)last_sector);
    

/////////////////////////////////
flash size = 0x00400000
writing offset = 0x002e0000
writing bin size = 0x120000
SPI_FLASH_SEC_SIZE = 4096 = 0x1000

/////////////////////////////////

SPIFFS Writing partition: type 1, subtype 130, offset 0x002e0000

W (29637) esp_ota_ops: OTA image wrote_size = 0, size = 100, wrote_size+size = 100 (first 0x00, last 0x00)
W (29717) esp_ota_ops: OTA image wrote_size = 100, size = 100, wrote_size+size = 200 (first 0x00, last 0x00)
W (29717) esp_ota_ops: OTA image wrote_size = 200, size = 100, wrote_size+size = 300 (first 0x00, last 0x00)
W (29727) esp_ota_ops: OTA image wrote_size = 300, size = 100, wrote_size+size = 400 (first 0x00, last 0x00)
W (29747) esp_ota_ops: OTA image wrote_size = 400, size = 100, wrote_size+size = 500 (first 0x00, last 0x00)
W (29747) esp_ota_ops: OTA image wrote_size = 500, size = 100, wrote_size+size = 600 (first 0x00, last 0x00)
W (29757) esp_ota_ops: OTA image wrote_size = 600, size = 100, wrote_size+size = 700 (first 0x00, last 0x00)
W (29777) esp_ota_ops: OTA image wrote_size = 700, size = 100, wrote_size+size = 800 (first 0x00, last 0x00)
W (29777) esp_ota_ops: OTA image wrote_size = 800, size = 100, wrote_size+size = 900 (first 0x00, last 0x00)
W (29787) esp_ota_ops: OTA image wrote_size = 900, size = 100, wrote_size+size = A00 (first 0x00, last 0x00)
W (29807) esp_ota_ops: OTA image wrote_size = A00, size = 100, wrote_size+size = B00 (first 0x00, last 0x00)
W (29807) esp_ota_ops: OTA image wrote_size = B00, size = 100, wrote_size+size = C00 (first 0x00, last 0x00)
W (29817) esp_ota_ops: OTA image wrote_size = C00, size = 100, wrote_size+size = D00 (first 0x00, last 0x00)
W (29837) esp_ota_ops: OTA image wrote_size = D00, size = 100, wrote_size+size = E00 (first 0x00, last 0x00)
W (29837) esp_ota_ops: OTA image wrote_size = E00, size = 100, wrote_size+size = F00 (first 0x00, last 0x00)
W (29847) esp_ota_ops: OTA image wrote_size = F00, size = 100, wrote_size+size = 1000 (first 0x00, last 0x01)
W (29937) esp_ota_ops: OTA image wrote_size = 1000, size = 100, wrote_size+size = 1100 (first 0x01, last 0x01)
W (30007) esp_ota_ops: OTA image wrote_size = 1100, size = 100, wrote_size+size = 1200 (first 0x01, last 0x01)
W (30007) esp_ota_ops: OTA image wrote_size = 1200, size = 100, wrote_size+size = 1300 (first 0x01, last 0x01)
W (30017) esp_ota_ops: OTA image wrote_size = 1300, size = 100, wrote_size+size = 1400 (first 0x01, last 0x01)
W (30037) esp_ota_ops: OTA image wrote_size = 1400, size = 100, wrote_size+size = 1500 (first 0x01, last 0x01)
W (30037) esp_ota_ops: OTA image wrote_size = 1500, size = 100, wrote_size+size = 1600 (first 0x01, last 0x01)
W (30047) esp_ota_ops: OTA image wrote_size = 1600, size = 100, wrote_size+size = 1700 (first 0x01, last 0x01)
W (30067) esp_ota_ops: OTA image wrote_size = 1700, size = 100, wrote_size+size = 1800 (first 0x01, last 0x01)
W (30067) esp_ota_ops: OTA image wrote_size = 1800, size = 100, wrote_size+size = 1900 (first 0x01, last 0x01)
W (30077) esp_ota_ops: OTA image wrote_size = 1900, size = 100, wrote_size+size = 1A00 (first 0x01, last 0x01)
W (30087) esp_ota_ops: OTA image wrote_size = 1A00, size = 100, wrote_size+size = 1B00 (first 0x01, last 0x01)
W (30097) esp_ota_ops: OTA image wrote_size = 1B00, size = 100, wrote_size+size = 1C00 (first 0x01, last 0x01)
W (30117) esp_ota_ops: OTA image wrote_size = 1C00, size = 100, wrote_size+size = 1D00 (first 0x01, last 0x01)
W (30117) esp_ota_ops: OTA image wrote_size = 1D00, size = 100, wrote_size+size = 1E00 (first 0x01, last 0x01)
W (30127) esp_ota_ops: OTA image wrote_size = 1E00, size = 100, wrote_size+size = 1F00 (first 0x01, last 0x01)
W (30147) esp_ota_ops: OTA image wrote_size = 1F00, size = 100, wrote_size+size = 2000 (first 0x01, last 0x02)
W (30227) esp_ota_ops: OTA image wrote_size = 2000, size = 100, wrote_size+size = 2100 (first 0x02, last 0x02)
W (30297) esp_ota_ops: OTA image wrote_size = 2100, size = 100, wrote_size+size = 2200 (first 0x02, last 0x02)
W (30297) esp_ota_ops: OTA image wrote_size = 2200, size = 100, wrote_size+size = 2300 (first 0x02, last 0x02)
W (30307) esp_ota_ops: OTA image wrote_size = 2300, size = 100, wrote_size+size = 2400 (first 0x02, last 0x02)
W (30317) esp_ota_ops: OTA image wrote_size = 2400, size = 100, wrote_size+size = 2500 (first 0x02, last 0x02)
W (30327) esp_ota_ops: OTA image wrote_size = 2500, size = 100, wrote_size+size = 2600 (first 0x02, last 0x02)
W (30347) esp_ota_ops: OTA image wrote_size = 2600, size = 100, wrote_size+size = 2700 (first 0x02, last 0x02)
W (30347) esp_ota_ops: OTA image wrote_size = 2700, size = 100, wrote_size+size = 2800 (first 0x02, last 0x02)
W (30357) esp_ota_ops: OTA image wrote_size = 2800, size = 100, wrote_size+size = 2900 (first 0x02, last 0x02)
W (30367) esp_ota_ops: OTA image wrote_size = 2900, size = 100, wrote_size+size = 2A00 (first 0x02, last 0x02)
W (30377) esp_ota_ops: OTA image wrote_size = 2A00, size = 100, wrote_size+size = 2B00 (first 0x02, last 0x02)
W (30397) esp_ota_ops: OTA image wrote_size = 2B00, size = 100, wrote_size+size = 2C00 (first 0x02, last 0x02)
W (30397) esp_ota_ops: OTA image wrote_size = 2C00, size = 100, wrote_size+size = 2D00 (first 0x02, last 0x02)
W (30407) esp_ota_ops: OTA image wrote_size = 2D00, size = 100, wrote_size+size = 2E00 (first 0x02, last 0x02)
W (30427) esp_ota_ops: OTA image wrote_size = 2E00, size = 100, wrote_size+size = 2F00 (first 0x02, last 0x02)
W (30437) esp_ota_ops: OTA image wrote_size = 2F00, size = 100, wrote_size+size = 3000 (first 0x02, last 0x03)
W (30527) esp_ota_ops: OTA image wrote_size = 3000, size = 100, wrote_size+size = 3100 (first 0x03, last 0x03)
W (30577) esp_ota_ops: OTA image wrote_size = 3100, size = 100, wrote_size+size = 3200 (first 0x03, last 0x03)
W (30577) esp_ota_ops: OTA image wrote_size = 3200, size = 100, wrote_size+size = 3300 (first 0x03, last 0x03)
W (30587) esp_ota_ops: OTA image wrote_size = 3300, size = 100, wrote_size+size = 3400 (first 0x03, last 0x03)
W (30607) esp_ota_ops: OTA image wrote_size = 3400, size = 100, wrote_size+size = 3500 (first 0x03, last 0x03)
W (30607) esp_ota_ops: OTA image wrote_size = 3500, size = 100, wrote_size+size = 3600 (first 0x03, last 0x03)
W (30617) esp_ota_ops: OTA image wrote_size = 3600, size = 100, wrote_size+size = 3700 (first 0x03, last 0x03)
W (30637) esp_ota_ops: OTA image wrote_size = 3700, size = 100, wrote_size+size = 3800 (first 0x03, last 0x03)
W (30637) esp_ota_ops: OTA image wrote_size = 3800, size = 100, wrote_size+size = 3900 (first 0x03, last 0x03)
W (30647) esp_ota_ops: OTA image wrote_size = 3900, size = 100, wrote_size+size = 3A00 (first 0x03, last 0x03)
W (30667) esp_ota_ops: OTA image wrote_size = 3A00, size = 100, wrote_size+size = 3B00 (first 0x03, last 0x03)
W (30667) esp_ota_ops: OTA image wrote_size = 3B00, size = 100, wrote_size+size = 3C00 (first 0x03, last 0x03)
W (30677) esp_ota_ops: OTA image wrote_size = 3C00, size = 100, wrote_size+size = 3D00 (first 0x03, last 0x03)
W (30697) esp_ota_ops: OTA image wrote_size = 3D00, size = FC, wrote_size+size = 3DFC (first 0x03, last 0x03)
W (30727) esp_ota_ops: OTA image wrote_size = 3DFC, size = 100, wrote_size+size = 3EFC (first 0x03, last 0x03)
W (30727) esp_ota_ops: OTA image wrote_size = 3EFC, size = 100, wrote_size+size = 3FFC (first 0x03, last 0x03)
W (30737) esp_ota_ops: OTA image wrote_size = 3FFC, size = 100, wrote_size+size = 40FC (first 0x03, last 0x04)
W (30787) esp_ota_ops: OTA image wrote_size = 40FC, size = 100, wrote_size+size = 41FC (first 0x04, last 0x04)
W (30787) esp_ota_ops: OTA image wrote_size = 41FC, size = 100, wrote_size+size = 42FC (first 0x04, last 0x04)
W (30797) esp_ota_ops: OTA image wrote_size = 42FC, size = 100, wrote_size+size = 43FC (first 0x04, last 0x04)
W (30817) esp_ota_ops: OTA image wrote_size = 43FC, size = 100, wrote_size+size = 44FC (first 0x04, last 0x04)
W (30817) esp_ota_ops: OTA image wrote_size = 44FC, size = 100, wrote_size+size = 45FC (first 0x04, last 0x04)

...

W (92717) esp_ota_ops: OTA image wrote_size = 11F6FC, size = 100, wrote_size+size = 11F7FC (first 0x11F, last 0x11F)
W (92727) esp_ota_ops: OTA image wrote_size = 11F7FC, size = 100, wrote_size+size = 11F8FC (first 0x11F, last 0x11F)
W (92747) esp_ota_ops: OTA image wrote_size = 11F8FC, size = 100, wrote_size+size = 11F9FC (first 0x11F, last 0x11F)
W (92757) esp_ota_ops: OTA image wrote_size = 11F9FC, size = 100, wrote_size+size = 11FAFC (first 0x11F, last 0x11F)
W (92767) esp_ota_ops: OTA image wrote_size = 11FAFC, size = 100, wrote_size+size = 11FBFC (first 0x11F, last 0x11F)
W (92777) esp_ota_ops: OTA image wrote_size = 11FBFC, size = 100, wrote_size+size = 11FCFC (first 0x11F, last 0x11F)
W (92787) esp_ota_ops: OTA image wrote_size = 11FCFC, size = 100, wrote_size+size = 11FDFC (first 0x11F, last 0x11F)
W (92797) esp_ota_ops: OTA image wrote_size = 11FDFC, size = 100, wrote_size+size = 11FEFC (first 0x11F, last 0x11F)
W (92807) esp_ota_ops: OTA image wrote_size = 11FEFC, size = 100, wrote_size+size = 11FFFC (first 0x11F, last 0x11F)
W (92817) esp_ota_ops: OTA image wrote_size = 11FFFC, size = 4, wrote_size+size = 120000 (first 0x11F, last 0x120)

/////////////////////////////////

as you see, the writes thas "just fill a sector to the end" alsways already end up erasing already the next sector too..
this might be a bigger bug than expected

imagine you write from beginning of a sector just a sector full. the following sector gets deleted

@kohait00
Copy link
Contributor Author

kohait00 commented Oct 29, 2023

so the fix turns out to be even simpler..

            uint32_t last_sector = (it->wrote_size + size - 1) / SPI_FLASH_SEC_SIZE; //last affected sector

which produces first/last sector ids for just the affected data.
@mahavirj this works even for your case.

I committed a new version of the fix a necessary precondition check..

RFC..

@mahavirj
Copy link
Member

@kohait00

Okay, thanks for providing more information.

To summarize, OTA update would fail if firmware_size == partition_size, because the code tries to erase one additional sector beyond the space reserved for the firmware partition.

Left one comment, please squash the commits post update.

Thanks.

@kohait00
Copy link
Contributor Author

corrected and squashed

Comment on lines +200 to +204
if(size == 0)
{
ESP_LOGD(TAG, "write data size is 0");
return ESP_OK;
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if(size == 0)
{
ESP_LOGD(TAG, "write data size is 0");
return ESP_OK;
}
if (size == 0) {
ESP_LOGD(TAG, "write data size is 0");
return ESP_OK;
}

Copy link
Member

Choose a reason for hiding this comment

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

@kohait00 You missed my suggestion in the previous update. Please see our coding style guidelines here.

@mahavirj
Copy link
Member

@kohait00 Also, please check the commit title and the description once. Right now, it still points to a squashed summary of different commit.

Otherwise, LGTM!

@kohait00
Copy link
Contributor Author

@kohait00

Okay, thanks for providing more information.

To summarize, OTA update would fail if firmware_size == partition_size, because the code tries to erase one additional sector beyond the space reserved for the firmware partition.

Left one comment, please squash the commits post update.

Thanks.

Exactly that

@kohait00 kohait00 changed the title fixing OTA on partition just at the end of the flash (IDFGH-11311) fixing OTA write up to SPI_FLASH_SEC_SIZE margins (IDFGH-11311) Oct 31, 2023
@mahavirj
Copy link
Member

mahavirj commented Nov 3, 2023

sha=61699ba6d67427679132528250e17befcd141002

@mahavirj mahavirj added the PR-Sync-Merge Pull request sync as merge commit label Nov 3, 2023
@espressif-bot espressif-bot added Status: Done Issue is done internally Resolution: NA Issue resolution is unavailable and removed Status: In Progress Work is in progress labels Nov 21, 2023
movsb pushed a commit to movsb/esp-idf that referenced this pull request Dec 1, 2023
…ed size

OTA update used to fail if `firmware_size == partition_size`, because the code was trying to
erase one additional sector beyond the space reserved for the firmware partition.

This commit fixes the problem and OTA update can work if the firmware
size exactly matches the allocated partition size.

Closes espressif#12460
espressif-bot pushed a commit that referenced this pull request Dec 8, 2023
…ed size

OTA update used to fail if `firmware_size == partition_size`, because the code was trying to
erase one additional sector beyond the space reserved for the firmware partition.

This commit fixes the problem and OTA update can work if the firmware
size exactly matches the allocated partition size.

Closes #12460
espressif-bot pushed a commit that referenced this pull request Dec 15, 2023
…ed size

OTA update used to fail if `firmware_size == partition_size`, because the code was trying to
erase one additional sector beyond the space reserved for the firmware partition.

This commit fixes the problem and OTA update can work if the firmware
size exactly matches the allocated partition size.

Closes #12460
espressif-bot pushed a commit that referenced this pull request Dec 16, 2023
…ed size

OTA update used to fail if `firmware_size == partition_size`, because the code was trying to
erase one additional sector beyond the space reserved for the firmware partition.

This commit fixes the problem and OTA update can work if the firmware
size exactly matches the allocated partition size.

Closes #12460
espressif-bot pushed a commit that referenced this pull request Dec 18, 2023
The size of partition of type APP should be multiple of 4 KB. Partition
generation tool now make this as a mandatory requirement. This is
minimum flash erase size. If the size of the APP type partition is not
aligned to 4 KB then the last erase operation could go beyond the allocated
partition and hence may fail. This issue would only be observed when the
firmware size grows very close to the allocated partition size, and hence
causing the OTA update to fail.

For already deployed devices on-field with the size of APP partition not
aligned to flash sector boundary, it is best to ensure that firmware
size always remains within the lower 4 KB boundary of the total
allocated space. While migrating to ESP-IDF 5.3 release, partition table
for an existing project can be adjusted accordingly for the build to
succeed.

Found during discussion in #12460
espressif-bot pushed a commit that referenced this pull request Dec 20, 2023
…ed size

OTA update used to fail if `firmware_size == partition_size`, because the code was trying to
erase one additional sector beyond the space reserved for the firmware partition.

This commit fixes the problem and OTA update can work if the firmware
size exactly matches the allocated partition size.

Closes #12460
hathach pushed a commit to adafruit/esp-idf that referenced this pull request Mar 27, 2024
…ed size

OTA update used to fail if `firmware_size == partition_size`, because the code was trying to
erase one additional sector beyond the space reserved for the firmware partition.

This commit fixes the problem and OTA update can work if the firmware
size exactly matches the allocated partition size.

Closes espressif#12460
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PR-Sync-Merge Pull request sync as merge commit Resolution: NA Issue resolution is unavailable Status: Done Issue is done internally
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants